Skip to content
MangoPlexPublic

About

Our Discord Bot

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

PENGUIN

penguin is distributed as a Docker image through GitHub Container Registry.

Run

Pull the latest release and supply the Discord credentials through environment variables:

docker pull ghcr.io/mangoplex/penguin:latest
docker run --rm \
  -e DISCORD_TOKEN='your-discord-token' \
  -e GUILD_ID='your-discord-guild-id' \
  ghcr.io/mangoplex/penguin:latest

Use a version tag such as v1.2.7 instead of latest to pin a release.

The container runs as an unprivileged user and does not store credentials.

Dynamic config

Features that aren't security-sensitive (e.g. which channel the Vietnam price tracker posts to) are controlled by a config.toml file rather than environment variables. Copy config.sample.toml to config.toml, edit it, and mount it into the container:

docker run --rm \
  -e DISCORD_TOKEN='your-discord-token' \
  -e GUILD_ID='your-discord-guild-id' \
  -v "$(pwd)/config.toml:/config.toml:ro" \
  ghcr.io/mangoplex/penguin:latest

The bot polls config.toml for changes every 10 seconds and reloads it automatically — no restart needed. The bot reads config.toml from its working directory, which is / in the container.

Price tracker

Set fuel_channel_id and/or electricity_channel_id under [price_tracker] in config.toml to Discord channel IDs. Leave a key unset to disable that tracker.

  • Fuel (via giaxanghomnay.com) and electricity (via quanlydien.com, which tracks official Ministry of Industry and Trade decisions) are both checked once a week, Thursday 15:05 local time (UTC+7), right after the fixed weekly adjustment slot.

Fuel posts a weekly embed every Thursday, including each item's change since the prior published snapshot, even if prices are unchanged. Electricity stores its first successful response as a baseline, then posts only when a tariff changes.

Failed fetches retry after 1, 5, 15, and 60 minutes, then hourly for up to 24 hours from the original check. Each attempt has a 30-second timeout. Disabling a tracker cancels its pending fetch/retries. Config reloads do not start overlapping retry chains. Discord send failures are logged without automatic resend because a failed response can still mean a delivered message.

Birthdays

Set channel_id under [birthday] to the Discord channel for announcements, and list people in people. Leave channel_id unset to disable the feature.

[birthday]
channel_id = 123456789012345678
people = [
  { name = "Alice", month = 1, day = 15 },
  # user_id is optional and mentions them instead of printing the name.
  { name = "Bob", month = 3, day = 8, user_id = 123456789012345678 },
]

An announcement is posted every day at announce_hour (0-23, default 8) local time (UTC+7) for everyone whose birthday it is. February 29 birthdays are celebrated on February 28 in years without a leap day. Entries with an invalid date, or an announce_hour outside 0-23, are ignored with a warning. Edits apply on the next config reload; a restart or reload never re-announces, so a restart after the announce hour skips that day's post.

Price cache

Set VALKEY_URL (e.g. redis://localhost:6379) to have the price/tariff trackers persist their last-seen snapshot in Valkey (or any Redis-compatible store) instead of only in memory. This survives bot restarts, so a restart around the weekly fuel check doesn't lose the baseline needed to detect a change. Each snapshot is stored with a last_updated timestamp.

Fuel stores its published Thursday period with the snapshot. Startup and config reloads recover a missing weekly post only through Friday 15:05 UTC+7; already published periods are skipped. Older cache entries infer their period from last_updated until the next successful post writes an explicit period. Electricity uses its first successful snapshot as a silent baseline and posts only later changes. Leave VALKEY_URL unset to keep the snapshots in memory; restarting then creates a new baseline. Duplicate suppression across restarts requires working Valkey persistence and a single bot instance. A crash after Discord accepts a message but before the cache is saved can still cause a duplicate after restart; exactly-once delivery is not guaranteed.

Build locally

docker build -t penguin .

Tagged releases

Pushing a tag named v<version> starts the GitHub Actions workflow. The tag must match the package version in Cargo.toml. A successful build publishes ghcr.io/mangoplex/penguin:<tag> and updates ghcr.io/mangoplex/penguin:latest.

About

Our Discord Bot

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages