Telegram bot that saves links from users and periodically sends one random saved link back after every SEND_EVERY_N saved links.
- User sends a full
http://orhttps://URL. - The bot validates and saves the link for that Telegram user.
- Duplicate links are ignored and reported to the user.
- The bot increments the user's saved-link counter.
- If the counter is divisible by
SEND_EVERY_N, the bot sends a random saved link. /rndsends a random saved link manually./startand/helpshow help text.
The project keeps the existing package layout:
app/- application bootstrap, dependency wiring, logger, graceful shutdown.clients/telegram- Telegram HTTP client.consumer/poller- event polling orchestration.events/telegram- Telegram event parsing and command routing.service/- business logic for links, counters, URL validation, random-send decision.storage/- storage interface plus sqlite and files implementations.config/- environment configuration.lib/e- small error wrapping helpers.
More detail is in ARCHITECTURE.md.
Use .env.example as a template, then export variables in your shell or process manager:
cp .env.example .env
set -a
source .env
set +aThe application reads os.Getenv only. It does not auto-load .env.
Required variables:
BOT_TOKEN- Telegram bot token from BotFather.DATABASE_PATH- sqlite database file path, for exampledata/sqlite/storage.db.SEND_EVERY_N- send a random saved link after every N saved links.
Optional variables:
LOG_LEVEL-debug,info,warn, orerror. Defaults toinfo.ENV-local,dev,staging, orproduction. Defaults tolocal.
DATABASE_URL is accepted as a fallback for DATABASE_PATH.
TELEGRAM_HOST is optional and defaults to api.telegram.org. It may also be set to a full base URL for testing through a local proxy or mock server. Invalid values fail startup with a regular configuration error.
Export env variables, then run:
go run .Or:
make runThe app creates the sqlite directory and schema on startup.
go test ./...
go test -race ./...Convenience target:
make testtest -z "$(gofmt -l .)"
go vet ./...Or:
make lintProduction storage is sqlite. It creates:
linkswithuser_id,link,created_at, and a unique(user_id, link)constraint.message_counterswithuser_id,count, andupdated_at.bot_statefor internal runtime state such as the persisted Telegram update offset.
The files storage remains available as a lightweight implementation of the storage interface, but sqlite is the recommended production backend.
main.gostays thin and delegates bootstrap intoapp.Run().- Import paths are lowercase and package names avoid underscores and hyphens.
- Runtime dependencies are wired once in
app/, while transport and business logic remain isolated. - Telegram update acknowledgements are persisted in sqlite after successful processing, which avoids replaying the full unconfirmed batch after restart.
GitHub Actions workflow is in .github/workflows/ci.yml and runs:
go mod downloadgofmtcheckgo vet ./...go test ./...go test -race ./...
- Static docs site source is in
docs/. - GitHub Pages deployment workflow is in
.github/workflows/pages.yml. - After enabling Pages with the
GitHub Actionssource in repository settings, pushes tomainpublish the docs site automatically.
BOT_TOKEN is required: exportBOT_TOKENin the environment before running.DATABASE_PATH or DATABASE_URL is required: set a sqlite path.invalid telegram api base url: fixTELEGRAM_HOSTso it is a valid host or full base URL.duplicate link: the bot ignores repeated links for the same user.- No random link is sent: verify
SEND_EVERY_Nand that the user has saved links. - sqlite build errors:
github.com/mattn/go-sqlite3uses CGO, so install a C compiler in the runtime/build image.