Small Discord bot intended to be self-hosted.
Optionally loads Azur Lane game data collected by the Azur Lane Data Collector in this repo.
This bot uses serenity and poise "next" versions, so there may be sudden internal changes when those update.
The minimum setup requires setting the environment variable DISCORD__TOKEN
to your Discord bot token (note: 2 underscores).
Upon startup, it will register its commands globally.
Configuration is supported either via environment variables or a file named houston_app.toml
in the working directory. The TOML config has this structure:
[discord]
# this is the same as the DISCORD__TOKEN env variable. only one must be specified.
token = "..."
[bot]
# relative or absolute path to the data produced by the Azur Lane Data Collector.
# optional. when not present, disables the Azur Lane module.
azur_lane_data = "azur_lane_data"
# sets the color used for most embeds.
# optional. defaults to 0xDDA0DD
embed_color = 0xDDA0DD
To configure logging, see the Logging section further down.
Additionally, based on the environment variable HOUSTON_PROFILE
, it will also load houston_app.$(HOUSTON_PROFILE).toml
. Its properties will take priority over the main config file. If the environment variable isn't set, it is considered to be release
.
Here is a quick overview of the supported commands:
Command | Description |
---|---|
calc | Evaluates a mathematical equation. |
config | Provides (temporary) configuration for this app. |
config hide | Configures whether responses to your commands are hidden from other users. |
coin | Flips a coin. |
dice | Rolls some dice. |
timestamp | Provides methods for localized timestamps. |
timestamp in | Gets a timestamp offset from the current time. |
timestamp at | Gets a timestamp at the specified time. |
timestamp of | Gets the creation timestamp from a Discord snowflake. |
upload | Uploads a file to an ephemeral message. Allows sharing if you are logged into multiple devices. |
who | Returns basic information about the provided user. |
The following commands are supported in context menus:
Command | Description |
---|---|
Get as Quote | (Message) Copies a format that is appropriate to use as a quote to crosspost. |
User Info | (User) Equivalent to /who . |
Additionally, when Azur Lane data is loaded, the azur command becomes available. Commands accepting names support fuzzy autocomplete.
Command | Description |
---|---|
azur | Information about mobile game Azur Lane. |
azur ship | Shows information about a ship. |
azur search-ship | Searches for ships. |
azur equip | Shows information about equipment. |
azur search-equip | Searches for equipment. |
azur augment | Shows information about an augment module. |
azur search-augment | Searches for augment modules. |
azur reload-time | Calculates the actual reload time for a weapon. |
The following features are optional and require a MongoDB database. Configure its URI in the config, f.e.:
[bot]
mongodb_uri = "mongodb://localhost/houston-tools"
Starboard will forward messages with a certain amount of reactions to another channel. Furthermore, for each board, it will track a leaderboard score.
Starboard must be configured:
# the first numeric key is the guild id.
# the second is the board id.
[bot.starboard.1293210831923974204.boards.1]
name = "starboard"
channel = 1305620816272166962
emoji = "⭐"
reacts = 3
notices = [
"An amazing post, {user}!",
"{user}, the stars aligned.",
]
[bot.starboard.1293210831923974204.boards.2]
name = "ripboard"
channel = 1305620834450407606
emoji = "wowie:1305835613790146631"
reacts = 3
notices = [
"What a stinker, {user}!",
"{user}, please stop.",
]
The board ID is used database-side to identify the board globally. As such, the board ID must be unique globally, not just per guild. Moving a board to another channel or emoji may have side effects but it won't break the scores.
The board emoji must either be a unicode emoji or "<name>:<id>", i.e. "wowie:1305835613790146631". The board emojis must be unique per guild. Unicode emojis are matched exactly, while custom emojis are matched by ID. The bot must be able to post to the channel.
The board channel is not required to be unique and multiple boards may use the same channel.
Also note that messages in nsfw channels are still tracked for sfw board channels, so they will show up on the leaderboards, but they will not be forwarded. Messages will be forwarded from an nsfw channel only if the board is nsfw.
The name is purely cosmetic and may be displayed in places where a channel name may be expected but channel mentions aren't valid.
The following commands will be enabled:
Command | Description |
---|---|
starboard top | Shows a board's top users. |
starboard top-posts | Shows the most-reacted posts in a board. |
Perks enables a currency system and a store to buy perks with.
This comes with the following configuration:
[bot.perks]
# sets the display name of the currency
cash_name = "$"
[[bot.starboard.1293210831923974204.boards]]
...
# in addition to the other options, you can also specify this on starboards.
# users will get as much currency per vote as specified here.
cash_gain = 2
# collectible enables an item with no purpose.
# it can be repeatedly bought in the perk store.
[bot.perks.collectible]
name = "Crab Plushy 🦀"
description = "Necessary for every rustacean."
cost = 4
# you may also set prize roles for owning the collectible
[bot.perks.collectible.1293210831923974204]
# the notice part is optional
# if set, you need both the channel and text
notice.channel = 1293210831923974207
notice.text = "Look, look! {user} reached {role}!"
# these are pairs of (needed, role_id)
# only checked on purchase
prize_roles = [
[20, 1309970796516610119],
[40, 1309970817882259498],
[80, 1309970846491742339],
[160, 1309970845531246633],
]
# rainbow enables rainbow roles.
# the color will only cycle when an event is received, at most once every 5 minutes.
[bot.perks.rainbow]
cost = 20
# duration is specified as HH:MM:SS.
duration = "24:00:00"
# configures a role for a server.
# the bot must have "Manage Roles" and have its role placed above it for this to work correctly.
# the perk will only be purchasable in servers configured here.
1293210831923974204.role = 1305905884807041124
# pushpin enables an item that lets someone pin/unpin a message
# to use the item, they need to use the context menu commands
[bot.perks.pushpin]
name = "Pushpin"
description = "Allow pinning or unpinning a message."
cost = 40
# role_edit enables an item that lets someone edit their unique role
# to change the role, they need to use /role-edit while owning this item
# to set the role, an admin must use `/perk-admin unique-role`
[bot.perks.role_edit]
name = "Orb of Change"
description = "Allows editing your unique role color and/or name."
cost = 10
# birthday enables birthday reminders and "gifts"
# when someone's birthday begins, in every configured guild, they will get
# the birthday role and the configured gift, as well a message from the bot.
[bot.perks.birthday]
# optional. defaults to 24 hours. should always be at least 24 h.
duration = "24:00:00"
# define some regions here. the first is considered the default.
# this allows users to pick when they want their birthday to start.
[[bot.perks.birthday.regions]]
name = "Europe/Africa (UTC+0)"
time_offset = "0:00:00"
[[bot.perks.birthday.regions]]
name = "Americas (UTC-8)"
time_offset = "-8:00:00"
[[bot.perks.birthday.regions]]
name = "SEA (UTC+7)"
time_offset = "7:00:00"
# also configure a server
[bot.perks.birthday.1293210831923974204]
role = 1316802158070595725
notice.channel = 1293210831923974207
notice.text = "Happy birthday, {user}!"
# the gifts are pairs of (Item, amount).
# valid items are: Cash, Pushpin, RoleEdit, Collectible
gifts = [
["Cash", 500],
]
The following commands will be enabled:
Command | Description |
---|---|
birthday add | Add your birthday. |
birthday check | Checks your set birthday. |
birthday time-zone | Sets your birthday time zone. |
perk-admin enable | Enables a perk for a member. |
perk-admin disable | Disables a perk for a member. |
perk-admin list | List active perks of a member. |
perk-admin give | Gives a user items. |
perk-admin unique-role | Sets a user's unique role. Can be omitted to delete the association. |
role-edit | Edit your unique role. |
shop | View the server shop. |
wallet | View your server wallet. |
The following commands are supported in context menus:
Command | Description |
---|---|
Use Pushpin: Pin | (Message) Pin this message. |
Use Pushpin: Unpin | (Message) Unpin this message. |
Commands are only available when the corresponding perk is enabled.
This feature is enabled if either the starboard or perks are enabled.
The following commands will be enabled:
Command | Description |
---|---|
profile | View a member's server profile. |
The following commands are supported in context menus:
Command | Description |
---|---|
Server Profile | (User) Equivalent to /profile . |
Logging can be configured via the configuration file. Broadly, this is done via the "log" section, which corresponds to a log4rs configuration. The configuration isn't reloaded at runtime.
By default, it adds an appender with the name "default" and an encoder of kind "default". The "default" appender kind is a console logger. The "default" encoder kind provides the standard logging format for this application.
The only standard appender available is "rolling_file".
For the application specific loggers, the following things are available:
[log.root]
# if you want to add loggers, you need to specify
# the default explicitly, if you want to keep it.
appenders = ["default", "webhook"]
# the level defaults to "warn"
# for app modules, it defaults to "trace"
level = "warn"
# this section is optional and the "default" appender is always present
[log.appenders.default]
# color detection is performed, but you may override that
color = true
# the `encoder` field is also supported
[log.appenders.webhook]
# the "webhook" kind logs batched messages to a discord webhook
kind = "webhook"
# you must specify an encoder - we use the default format here
encoder.kind = "default"
# you must also specify the webhook url
url = "https://discord.com/api/webhooks/<snip>/<snip>"
# optional (32). specifies the limit of queued-up records before discarding new ones
buffer_size = 32
# optional (10). specifies the maximum amount of records combined into one message
batch_size = 10
Warning
The collector runs the game scripts. As should be common sense, do not run untrusted code.
This is a command line tool that loads Azur Lane game scripts and outputs data to be used and displayed by the Discord bot.
-i, --inputs <INPUTS>... The path that the game scripts live in
-o, --out <OUT> The output directory
--assets <ASSETS> The path that holds the game assets
-m, --minimize Minimize the output JSON file
--color <COLOR> Override whether this program outputs color [possible values: true, false]
-h, --help Print help
--inputs
is required. --out
defaults to azur_lane_data
.
--inputs
specifies a path to decompiled game scripts, including unpacked sharecfgdata
.
It is expected that sharecfgdata/<asset-type>.lua
will load all entries when executed.
If --assets
is specified, it will look for a folder within it named shipmodels
that is searched for Unity asset bundles for extracting chibi images of the ships.
In essence, if you copy the shipmodels
folder from the game's data and point to the parent directory, it should work.
If it is not specified, this step is skipped.
Currently the collector defaults to using Lua 5.4 rather than LuaJIT. This is in part due to unpacked sharecfgdata
files commonly being a merged decompilation output that cannot be loaded by LuaJIT due to too many constants.
If you want to switch to a different Lua edition, edit the enabled features of the mlua
dependency. This isn't perfectly supported, but if it compiles, it should be fine.
If you specify multiple input directories, the data is "merged". That is, ships, equipment, retrofits, and skins will added to earlier sets of data. The first set that contains a certain entry will take priority.
The program will print its terminal output to stderr, attempting to use ANSI escapes to improve the output. There is no stdout output.
By default, the program will try to detect whether the output supports colors. Notably, non-terminal outputs and environment that specify NO_COLOR
will not have color.
In case the detection ends up being wrong, you may pass --color=true
or --color=false
to override the detection.
This is a standard Rust workspace. If you are already familiar with Cargo and the Rust toolchain, you should not need any further instructions.
Install the stable Rust toolchain if you haven't already, then invoke cargo for release builds:
cargo build --release
Alternatively, you can run the executables directly as:
cargo run --bin houston_app
cargo run --bin azur_lane_data_collector -- --inputs ...
As present in this repository, the release builds specify some additional options:
- Fat LTO is enabled. Compilation may be slow, but the output should be better.
- Panics will abort rather than unwind.
Edit the workspace's Cargo.toml if you prefer other behavior.
MIT, see LICENSE.