Reliable Discord-client IRC-server Daemon (rdircd) - tool to use Discord via IRC client
Reliable Discord-client IRC Daemon (rdircd)
Table of Contents
- Description
- [WARNING]
- Features
- Limitations
- Usage
- [Links]
- [More info on third-party client blocking]
- API and Implementation Notes
Description
rdircd is a daemon that allows using a personal [Discord] account through an [IRC] client.
It translates all private chats and public channels/threads on "discord servers" into channels on an IRC server that it creates and that you can connect to using regular IRC client, instead of a browser or electron app.
"reliable" is only in the name because one of the initial goals was to make it confirm message delivery and notify about any issues in that regard, which was somewhat lacking in other similar clients at the time. Script's probably not actually that reliable and seem to be full of bugs in all less-used features, but those should be easy to fix if anyone cares enough to report them.
There's an IRC channel to talk about the thing - join [#rdircd at libera.chat].\ IRC URL:
See also [Links] section below for rarely-updated list of other alternative clients.
Repository URLs:
[Discord]: https://discord.gg/ [IRC]: https://en.wikipedia.org/wiki/InternetRelayChat [#rdircd at libera.chat]: https://web.libera.chat/?channels=#rdircd
WARNING
While I wouldn't call this app a "bot" or "automating standard user accounts" - intent here is not to post any automated messages or scrape anything - pretty sure Discord staff considers all third-party clients to be "bots", and requires them to use special second-class API (see [Bot vs User Accounts] section in API docs), where every account has to be separately approved by admins on every connected discord server/guild, making it effectively unusable for a random non-admin user.
This app does not present itself as a "bot" and does not use bot-specific endpoints, so using it can result in account termination if discovered.
See also [More info on third-party client blocking] section below.
You have been warned! :)
[Bot vs User Accounts]: https://discord.com/developers/docs/topics/oauth2#bot-vs-user-accounts
Features
- Reliable outgoing message ordering and delivery, with explicit notifications
- Support for both private and public channels, channel ordering, threads,
- Per-server and global catch-all channels to track general activity.
- Configurable local name aliases/renames, outgoing message blocks/replacements,
- Support for limited runtime reconfiguration via #rdircd.control channel.
- Simple and consistent discord to irc guild/channel/user name translation.
- Translation for discord mentions, replies, attachments, stickers and emojis
- Limited support for translation of discord user mentions and emojis in
- Very basic tracking for user presence - can set discord status when irc
- Easily accessible backlog via /topic (/t) commands in any channel, e.g.
/t log 2h to show last 2 hours of backlog or /t log 2019-01-08 to dump backlog
from that point on to the present, fetching in multiple batches if necessary.
- Messages sent through other means (e.g. browser) will be relayed to irc too,
- Full unicode support/use everywhere.
- IRC protocol is implemented from IRCv3 docs, but doesn't use any non-RFC stuff,
- Extensive protocol and debug logging options, some accessible at runtime via
- Single python3 script that only requires aiohttp module, trivial to run or
- Runs in relatively stable ~50M memory footprint on amd64, which is probably
- Easy to tweak and debug without rebuilds, gdb, rust and such.
Limitations
- Only user mentions and emojis sent from IRC are translated into discord tags
- No support for sending attachments or embeds of any kind - use WebUI for that, not IRC.
- No discord-specific actions beyond all kinds of reading and sending messages
- Creating new private chats and channel/forum threads is not supported.
- Does not emit user joins/parts events and handles irc /names in a very simple
- Completely ignores all non-text-chat stuff in general
- Discord tracks "read_state" server-side, which is not used here in any way -
/t log ... commands in chans)
or automatically for channels marked for that (/topic log watch or watch
in #rdircd.control), so can sometimes be easy to miss on quiet reconnects.
- Does not support discord multifactor authentication mode, but manual-token
- [Slash commands] (for bots) are not supported in any special way,
- Not the most user-friendly thing, though probably same as IRC itself.
- I only run it on Linux, so it's unlikely to "just work" on OSX/Windows, but idk.
- Custom ad-hoc implementation of both discord and irc, not benefitting from any
- Seem to be against Discord guidelines to use it - see WARNING section above for more details.
Usage
Requirements
- Python 3.9+
- aiohttp
- (Optional) [python-systemd] - only if using tweaks mentioned in [systemd integration] below.
password-hash=, might
also need to install [scrypt module] separately (via e.g. pkg_add py3-scrypt),
as python port there doesn't seem to have [hashlib.scrypt] in its stdlib.
aiohttp and its dependencies might also require installing [typing_extensions module] when using older major python versions (e.g. 3.10.x or earlier). Seem to also be an issue with OpenBSD's aiohttp or aiosignal packages (run pkgadd py3-typingextensions to fix).
[python-systemd]: https://github.com/systemd/python-systemd [scrypt module]: https://github.com/holgern/py-scrypt/ [hashlib.scrypt]: https://docs.python.org/3/library/hashlib.html#hashlib.scrypt [typingextensions module]: https://github.com/python/typingextensions
Installation
Simplest way might be to use package for/from your linux distribution, if it is available.
Currently known distro packages (as of 2020-05-17):
- Arch Linux (AUR):
- Termux (Android linux env): [termux/termux-packages/packages/rdircd]
Should be easy to install one script and its few dependencies manually as well, as described in the rest of this section below.
On debian/ubuntu, installing dependencies can be done with this one command:
# apt install --no-install-recommends python3-minimal python3-aiohttp
Other linux distros likely have similar packages as well, and I'd recommend trying to use these as a first option, so that they get updates and to avoid extra local maintenance burden, and only fallback to installing module(s) via "pip" if that fails.
On any arbitrary distro with python (python3) installed, using pip/venv to install aiohttp module (and its deps) to unprivileged "rdircd" user's home dir might work (which is also used to run rdircd in the next example below), but ignore this if you've already installed it via OS package manager or such:
root # useradd -m rdircd
root # su - rdircd
Option 1: use venv to install dependencies into "_venv" dir
rdircd % python3 -m venv _venv rdircd % ./_venv/bin/pip install aiohttp
Option 2: install pip (if missing) and use it directly
rdircd % python3 -m ensurepip --user rdircd % python3 -m pip install --user aiohttp
If you have/use [pipx] (e.g. from distro repos), it can be used to run python apps like this one and auto-maintain dependencies - just "pipx run" the main script: pipx run rdircd --help - without needing to touch venv or pip at all (pipx will do it "under the hood").
After requirements above are installed, script itself can be fetched from this repository and run like this:
## Ignore "useradd" if you've already created a user when running "pip" above
root # useradd -m rdircd
root # su - rdircd
If using "venv" install example above - load its env vars
Or alternatively run script via "./_venv/bin/python rdircd ..." command line
rdircd % source ./_venv/bin/activate
rdircd % curl -OL 'https://raw.githubusercontent.com/mk-fg/reliable-discord-client-irc-daemon/master/rdircd{,.unicode-emojis.txt.gz}' rdircd % chmod +x rdircd
Use "pipx run rdircd ..." here and below, if using pipx instead of pip/venv/distro-pkgs
rdircd % ./rdircd --help
...to test if it runs...
rdircd % ./rdircd --conf-dump-defaults ...for a full list of all supported options with some comments... ...alternatively, to create rdircd.ini template: ./rdircd -c rdircd.ini --conf-init
rdircd % nano rdircd.ini ...see below for configuration file info/example...
rdircd % ./rdircd --debug -c rdircd.ini ...drop --debug and use init system for a regular daemon...
For setting up daemon/script to run on OS boot, [rdircd.service] systemd unit file can be used in most Linux environments (maybe edit ExecStart= options and paths there, and see also [systemd integration] below), or otherwise probably via init.d script, or maybe in "screen" or "tmux" session as a last-resort ad-hoc option. Make sure it runs as e.g. "rdircd" user created in snippet above, not as root.
To update the script later, if needed, replace it with a latest version, e.g. via re-downloading with a curl command above, git-pull on the repo clone, docker-compose up --build, updating os package, or in some other way, usually related to how it got installed in the first place.
[termux/termux-packages/packages/rdircd]: https://github.com/termux/termux-packages/tree/master/packages/rdircd [Dockerfile]: Dockerfile [docker-compose.yml]: docker-compose.yml [README.docker-permissions.md]: README.docker-permissions.md [pipx]: https://pypa.github.io/pipx/ [rdircd.service]: rdircd.service
Setup and actual usage
Create configuration file with discord and ircd auth credentials in ~/.rdircd.ini (see all --conf... opts wrt these):
ini
[irc]
password = hunter2
[auth] email = discord-reg@email.com password = discord-password
Note: IRC password can be omitted, but make sure to firewall that port from everything in the system then (or maybe do it anyway).
If you set password though, maybe do not use IRC password= option like above, and use password-hash= and -H/--conf-pw-scrypt to generate it instead. Either way, make sure to use that password when configuring connection to this server in the IRC client as well.
Start rdircd daemon: ./rdircd --debug
Connect IRC client to "localhost:6667" - default listen/bind host and port.
(see ./rdircd --conf-dump-defaults or corresponding CLI -i/--irc-bind / -s/--irc-tls-pem-file options for binding on different host/port and TLS socket wrapping, for non-localhost connections)
Use IRC /list command to see channels for all joined discord servers/guilds:
Channel Users Topic ------- ----- ----- #rdircd.control 1 rdircd: control channel, type "help" for more info #rdircd.debug 1 rdircd: debug logging channel, read-only #rdircd.monitor 1 rdircd: read-only catch-all channel with messages from everywhere #rdircd.leftover 1 rdircd: read-only channel for any discord messages in channels ... #rdircd.voice 1 rdircd: read-only voice-chat notifications from all discords/channels #rdircd.monitor.jvpp 1 rdircd: read-only catch-all channel for discord [ Server-A ] #rdircd.leftover.jvpp 1 rdircd: read-only msgs for non-joined channels of discord [ Server-A ] ... #me.chat.SomeUser 1 me: private chat - SomeUser #me.chat.x2s456gl0t 3 me: private chat - some-other-user, another-user, user3 #jvpp.announcements 1 Server-A: Please keep this channel unmuted #jvpp.info 1 Server-A: #jvpp.rules 1 Server-A: #jvpp.welcome 1 Server-A: Mute unless you like notification spam ... #axsd.intro 1 Server-B: Server info and welcomes. #axsd.offtopic 1 Server-B: Anything goes. Civility is expected.
Notes on information here:
- Short base64 channel prefix is a persistent id of the discord guild that it belongs to.
- Full guild name (e.g. "Server-A") is used as a prefix for every channel topic.
- "#me." is a prefix of discord @me guild, where all private channels are.
- #rdircd.control and #rdircd.debug are special channels, send
helpthere for more info. - There's #rdircd.monitor catch-all channel and guild-specific ones (see notes below).
- #rdircd.leftover channels are like #rdircd.monitor, but skip msgs from already-joined channels.
- #rdircd.voice is also there to monitor only voice channel notifications from everywhere.
- Public IRC channel users are transient and only listed/counted if they sent
- Everything in that /list and everything used to talk through this app are IRC
- Channels always list at least 1 user, to avoid clients hiding ones with 0.
/j #axsd.offtopic (/join) as you'd do with regular IRC to see/post msgs there.\
Channels joins/parts in IRC side do not affect discord in any way.
Run /topic (often works as /t) irc-command to show more info on channel-specific commands, e.g. /t log to fetch and replay backlog starting from last event before last rdircd shutdown, /t log list to list all activity timestamps that rdircd tracks, or /t log 2h to fetch/dump channel log for/from specific time(stamp/span) (iso8601 or a simple relative format).
Daemon control/config commands are available in #rdircd.control channel, #rdircd.debug chan can be used to tweak various logging and inspect daemon state and protocols more closely, send help there to list available commands.
For broad outline of various supported configuration settings, see [rdircd.defaults.ini] file (output of ./rdircd --conf-dump-defaults), and more on particular uses of those below.
See [Installation] section above for some options for how to setup this script to run in a more long-term way via OS package, systemd or some OCI container runtime like [docker] or [podman].
[rdircd.defaults.ini]: rdircd.defaults.ini [docker]: https://www.docker.com/ [podman]: https://podman.io/
Misc feature info
Notes on various optional and less obvious features are collected here.\ See "Usage" section for a more general information.
Multiple config files
Multiple ini files can be specified with -c option, overriding each other in sequence.
Last one will be updated wrt \[state\], token= and similar runtime stuff (by default, unless disabled), as well as any values set via #rdircd.control channel commands, so it can be useful to specify persistent config with auth and options, and separate (initially empty) one for such dynamic state.
E.g. ./rdircd -c config.ini -c state.ini will do that.\ --conf-dump can be added to print resulting ini assembled from all these.\ --conf-dump-defaults flag can be used to list all options and their defaults.
Frequent state timestamp updates are done in-place (small fixed-length values), but checking ctime before writes, so should be safe to edit any of these files manually anytime anyway.
Sending SIGHUP to the script or reload command in control-channel should load and apply values from all config files in the same order. Note that such operation won't reset any values missing in files to their defaults, only apply stuff explicitly set there on top of the current config.
Command-line --conf-ro and corresponding conf-readonly ini-file option can be used to avoid saving anything between restarts, though I'd recommend using writable config on tmpfs (e.g. under /run/rdircd/ or such) if it is due to read-only filesystem/image, which will at least keep changes until reboot.
Related token-temp option in \[auth\] section can be used to only keep auth token out of writable configs, intended for usage with at-rest-encrypted config file, but using more transient, revokable and IP-bound token + token-manual might be a better idea anyway (see [section on MFA/captchas below]).
[section on MFA/captchas below]: #hdr-captcha-solvingisrequiredtologin_for.ls9P
systemd integration
[rdircd.service] systemd unit file in the repository can be used as a template for running rdircd with [systemd] init daemon, which is used by most linux distros.
Edit User=rdircd, WorkingDirectory=, ReadWritePaths= and ExecStart= lines there to adjust it for local setup, i.e. which user/uid should be used to run it, where configuration files are stored, script path and its command line, as well as any other options as-needed.
In addition to that, if [python-systemd] bindings module is installed (e.g. python -c 'import systemd' doesn't print ImportError), then extra bits of integration can be enabled in that unit file:
rdircd.servicecan be started by [systemd .socket unit] file likerdircd.socket:
[Socket]
ListenStream=127.0.0.1:6667
[Install] WantedBy=sockets.target
To also have rdircd exit after IRC client disconnects (i.e. that socket no longer in use), set something like no-clients-exit-timeout = 2m in \[irc\] config file section, or via -I/--idle-exit-timeout command-line option.
No need to pass socket via stdin. This type of startup avoids any ordering issues.
Type=notifycan be used (instead ofType=exec) to inform systemd when
AF_UNIX has to be added to RestrictAddressFamilies= line if this is used.
FileDescriptorStoreMax=1can be set withType=notifyto use systemd
It will still disconnect all IRC clients on restart, but if those reconnect immediately afterwards, they won't get "Connection Refused" error and will be able to connect, as open IRC socket doesn't change or go away.
This shouldn't be needed if rdircd.socket startup described above is used, as having systemd handle socket explicitly does the same thing there.
All of this is optional, and systemd python module is only checked if Type=notify, fdstore or socket activation mechanisms are detected on startup from systemd-set environment variables.
[systemd]: https://systemd.io/ [systemd .socket unit]: https://man.archlinux.org/man/systemd.socket.5 [File Descriptor Store]: https://systemd.io/FILEDESCRIPTORSTORE/
Private chats
ALL chats in rdircd (and discord) are a channel.\ IRC's /q, /query and /msg cannot be used in an IRC-typical way.\ To talk in any private chat, join a channel like #me.chat.\
There is currently no way to create new private chats from rdircd, use other clients or WebUI for that (or ask someone to contact you first), but once private chat channel is created, it can be used in rdircd as well.
See also [Auto-joining channels], [Channel history] and/or [/join e.g. #rdircd.leftover.me channel] to monitor private messages reliably, if needed.
[/join e.g. #rdircd.leftover.me channel]: #hdr-rdircd.monitorandrdircd.leftover_channels
Channel commands
In all IRC channels representing a discord channel - send /topic (or /t - shorthand for it often supported in IRC clients) - which should print up-to-date info on all channel-specific commands, like those:
/t info- show some internal guild/channel information, like IDs and such for renames.
/t info {user-name...}- query info on user name (or part of it) in this discord.
/t info joe137 will lookup joe137 user on a discord server that
channel belongs to, printing info about them, like their [various discord names].
/t log [state]- replay history since "state" point (last rdircd stop by default).
/t log (same as /t log 1) can be used e.g. after rdircd restart
to query discord for any messages that might've been posted after it was stopped,
and before it was started back again (plus any others since then).\
Or /t log 0 to check history since last msg that rdircd has seen,
for cases when discord/network is flakey and something might've been lost that way.\
(where these 1 and 0 numbers refer to saved timestamps from /t log list
output, stored/updated under [state] in the ini file)
It can also be used with an absolute or relative time, e.g. /t log 15m to request/replay channel history within last 15 minutes, or /t log 2019-01-08 12:30 to replay history since that specific rdircd-local time (unless timezone is specified there too).
Just /t or /topic in any discord proxy channel will list more such commands and more info on how to use them.
Last message sent to a discord channel can be [edited using sed-replacement command] like s/hoogle/google/ to fix a typo or quickly amend/reword/clarify that last line.\ Or //del command can be used to delete it - see ["quick edits/deletes"] section below.
@silent prefix-command in messages can suppress user notifications about it (also [explained below somewhere]).
In special channels like #rdircd.control and #rdircd.debug: send h or help.\ They can have somewhat long list of supported commands, e.g. here are some of the commands for #rdircd.control:
status(orst) can be used to check on discord and irc connection infos.
connect/disconnect(oron/off) commands can be used to manually
set irc-disable-reacts-msgs yes- temp-disable discord
set command).\
Or set -s irc-disable-reacts-msgs yes to make it permanent
(-s/--save flag for saving value to ini config file), or simple set
without parameters to see all general configuration options and their values.
rx Block mee6 bot-noise = (?i)^<MEE6>- temp-block all messages from MEE6 bot.\
...and there are more of those - type help there for full up-to-date info.
[local renames]: #hdr-localnamealiases [various discord names]: #hdr-peoplesnamesondiscord [edited using sed-replacement command]: #hdr-quickeditsdeletesforjust-sent_messages ["quick edits/deletes"]: #hdr-quickeditsdeletesforjust-sent_messages [explained below somewhere]: #hdr-silentmessagesandothersuch_flags [section about this filtering]: #hdr-customfilteringforallreceived_messages [more examples of such stuff under tips-and-tricks]: #hdr-cutdownonvariouscommon_noise
#rdircd.monitor and #rdircd.leftover channels
#rdircd.monitor can be used to see activity from all connected servers - gets all messages, prefixed by the relevant irc channel name.
#rdircd.monitor.guild (where "guild" is a hash or alias, see above) is a similar catch-all channels for specific discord server/guild.
#rdircd.monitor.me can be useful, for example, to monitor any private chats and messages for discord account (see also [Auto-joining channels] example).
#rdircd.leftover and similar #rdircd.leftover.guild channels are like monitor channels, but skip messages from any channels that IRC client have JOIN-ed, including e.g. /join #rdircd.leftover.game-x hiding that "game-x" discord msgs from global catch-all #rdircd.leftover, but not counting #rdircd.monitor channels (i.e. joining them doesn't affect "leftover" ones in any way).
#rdircd.voice is a channel similar to #rdircd.monitor, but only catching voice-chat event notices, to be able to track those in a timely manner.
These channels can be ignored if not needed, or disabled entirely by setting e.g. chan-monitor to an empty value under \[irc\] ini config-file section. For example, per-discord voice-activity channels are default-disabled there.
Configuration file also has \[unmonitor\] section for an optional list of channel-names to ignore in monitor/leftover channels, for example:
ini
[unmonitor]
All filters are applied to channel names and are case-insensitive
Ignore this particular "bot-commands" channel = game-X.bot-commands
skip forum threads in "game-X" guild = glob:game-X.forum.=*
"wordle" threads in any guild (and chans ending in .wordle) = glob:*.wordle
Don't show threads in any forum-like channels = re:^[^.]+\.(forum|discuss)\.=.*
disregard all voice-chat stuff = glob:*.vc
Keys (as in part before "=") in such config section are ignored, and can be anything, e.g. comments explaining the patterns (like in example above), while values are either exact channel names (with discord prefix, optional #-prefix), or a "glob:"/"re:"-prefixed glob / regexp pattern ([shell-like globs] or [python regexps]), written as <some-key/comment> = glob:<wildcard-pattern> or <some-key/comment> = re:<regexp-pattern> lines - see examples just above.
Channel names matched by those filters will be dropped from monitor-channels, so this can be used to define a list of spammy things that you don't care about and don't want to see even there.
"unmonitor" (or "um") command in #rdircd.control can add/remove such filters on-the-fly anytime. See also match-counters config option to keep track of whether specific rule(s) are still needed/being-used.
Messages in monitor-channels are limited to specific length/lines, to avoid excessive flooding by long and/or multi-line msgs. "len-monitor" and "len-monitor-lines" parameters under \[irc\] config section can be used to control these limits, see ["./rdircd --conf-dump-defaults" output] for their default values. There are also options to change name format of monitor channels.
[shell-like globs]: https://docs.python.org/3/library/fnmatch.html [python regexps]: https://docs.python.org/3/library/re.html ["./rdircd --conf-dump-defaults" output]: rdircd.defaults.ini
People's names on discord
On IRC, everyone has one name, but that's [not the case with Discord], where each user can have following names:
login- discord "username", uniquely identifying every user.display- "display name" set by the user in discord account settings, not unique.nick- server and friend "nicknames", set in discord server settings, not always by you.
login is closest concept to IRC nicknames, as it's globally-unique,
consistent, short, ascii-only, and can be used by setting
name-preference-order = login option in \[discord\] section (not the default).
Official discord clients display other names first, which is why name-preference-order option defaults to nick display login value, which uses discord/friend-specific nicknames first, if any, falling back to free-form name that user set in account settings, and their login name otherwise.
Other things in fancy user-set nicknames that IRC doesn't allow also get replaced with common unicode characters, spaces with "·" middle-dots for example, or <> common irc-nick brackets with ◄► unicode arrows. Long Discord nicks are truncated.
There are no IRC notifications about users changing their discord-specific display/nick-names at the moment, and they don't have to be unique, which might make it hard to tell who-is-who, if they keep changing nicks for whatever reason.
All this is configurable via ini file settings (or in #rdircd.control channel), so if it gets too silly and maddening, set name-preference-order = login to use unique consistent IRC-friendly nicks for everyone instead.
IRC /who command or /topic info can help translating between these names, for example /t info john1234 can be used to print info for that name/login in the channel buffer, which should include all users with partial match of that name on that specific discord, while /who command searches all joined discords.
[not the case with Discord]: https://support.discord.com/hc/en-us/articles/12620128861463-New-Usernames-Display-Names
Local name aliases
(more like "renames" than "aliases", as old names don't continue to work for these)
Can be defined in the config file to replace hash-based discord prefixes or server channel names with something more readable/memorable or meaningful to you:
ini
[renames]
guild.jvpp = game-x
guild.sn3y = log-bot
guild.sn3y.chan-fmt = logs/{name}.log
chan.some-long-and-weird-name = weird
chan.@710035588048224269 = general-subs
user.noname123 = my-pal-bob
user.@123980071029577864 = joe
This should:
- Turn e.g. #jvpp.info into #game-x.info - lettersoup guild-id to more
- Change format for channel names of "sn3y" discord from something like
Format template uses [python str.format syntax] with "name" (channel name) and "prefix" (guild prefix - will be "log-bot" in this example) values. Default format is {prefix}.{name}.
This format option does not affect monitor/leftover channel name(s) (e.g. #rdircd.monitor.log-bot or #rdircd.leftover.game-x) - see "chan-monitor-guild" and "chan-leftover-guild" options under \[irc\] section for changing that.
- Rename that long channel to have a shorter name (retaining guild prefix) -
Note that this affects all guilds where such channel name exists, and source name should be in irc format, same as in /list, and is rfc1459-casemapped (same as on irc).
- Rename channel with id=710035588048224269 to "memes" (retaining guild prefix) -
That long discord channel identifier (also called "snowflake") can be found by typing /t info topic-command in corresponding irc channel, and can be used to refer to that specific channel, i.e. renaming this one #general on this one discord server instead of renaming all #general channels everywhere.
This is especially useful when two channels have same exact name within same discord, and normally will be assigned .<id-hash> non-descriptive suffixes.
- Rename couple users, referenced by their discord username and id.
/t info <nick-or-part-of-it> command in discord channel or similar /who
irc-command can help to [Lookup discord IDs], like ones used there.
Currently only listed types of renaming are implemented, for discord prefixes and channels, but there are also options under \[irc\] section to set names for system/monitor/leftover and private-chat channels - "chan-sys", "chan-private", "chan-monitor" and such (see ["./rdircd --conf-dump-defaults" output]).
Set chan-monitor-guild = {prefix} there for example, to have #game-x channel be catch-all for all messages in that discord, without default long #rdircd.monitor.\* prefix.
[python str.format syntax]: https://docs.python.org/3/library/string.html#format-string-syntax
Private messages and friends
Discord private messages create and get posted to channels in "me" server/guild, same as they do in discord webui, and can be interacted with in the same way as any other guild/channels (list, join/part, send/recv msgs, etc).
Join #rdircd.monitor.me (or #rdircd.monitor, see above) to get all new msgs/chats there, as well as relationship change notifications (friend requests/adds/removes) as notices.
Accepting friend requests and adding/removing these can be done via regular discord webui and is not implemented in this client in any special way.
See also [Auto-joining channels] section below for an easy way to pop-up new private chats in the IRC client via invites.
Discord channel threads / forums
"Threads" is a Discord feature, allowing non-admin users to create transient ad-hoc sub-channels anytime for specific topic, which are auto-removed ("archived") after a relatively-short inactivity timeout (like a day).
Discord "forum" channels are basically channels, where people can only create and talk in theads, with listing of those replacing default channel chatter.
All non-archived threads should be shown in rdircd channel list as a regular IRC channels, with names like #gg.general.=vot5.lets·discuss·stuff, extending parent chan name with thread id tag ("=vot5" in this example) and a possibly-truncated thread name (see "thread-chan-name-len" config option).
There are several options for how to see and interact with threads from the parent channel (mostly in \[discord\] section, [see --conf-dump-defaults output]):
ini
[irc]
thread-chan-name-len = 30
[discord] thread-id-prefix = = thread-msgs-in-parent-chan = yes thread-msgs-in-parent-chan-monitor = no thread-msgs-in-parent-chan-full-prefix = no thread-redirect-prefixed-responses-from-parent-chan = yes ...
But even with all these disabled, a simple notice should be sent to the channel when threads are started, so that one won't miss them entirely.
There's no support for creating new threads from IRC, unarchiving old ones or otherwise managing these, and joining thread channel in IRC doesn't actually "join thread" in Discord UI (pining it under channel name), but posting anything there should do that automatically.
[see --conf-dump-defaults output]: rdircd.defaults.ini
Auto-joining channels
"chan-auto-join-re" setting in \[irc\] section allows to specify regexp to match channel name (without # prefix) to auto-join when any messages appear in them.
For example, to auto-join any #me.\* channels (direct messages), following regular expression value ([python "re" syntax]) can be used:
ini
[irc]
chan-auto-join-re = ^me\.
Or to have irc client auto-join all channels, use chan-auto-join-re = .\ Empty value for this option (default) will match nothing.
This can be used as an alternative to tracking new stuff via #rdircd.monitor/leftover channels.
This regexp can be tweaked at runtime using set command in #rdircd.control channel, same as any other values, to e.g. temporary enable/disable this feature for specific discords or channels.
[python "re" syntax]: https://docs.python.org/3/library/re.html#regular-expression-syntax
Channel history
Usual IRC method for tracking history in general is by using an [IRC bouncer] like [ZNC] in-between server and client, or clients with similar component built-in (like [Quassel] or [The Lounge]), which should work with rdircd as well, and is probably the best way to do it for most use-cases.
Everything on discord is represented as IRC channels in rdircd (incl. 1-on-1 private chats), and topic-commands like /topic log 2h can be used in those to check server-side history backlog in a one-off manual way there - run /t help for more info.
For specific important chats, or if rdircd can't stay reliably connected to discord/internet, it's also possible to automatically fetch and replay history from discord servers, using /t log watch command or watch in #rdircd.control channel. Channels marked that way have an extra <W> tag in topic.
Discord-side backlog for those is automatically checked when IRC client/bouncer reconnects and when rdircd reconnects to discord, tracking last forwarded messages per-channel (under config file \[state\] section, can be printed via --conf-dump-state) vs last\message\id channel timestamps received from discord.
I'd recommend to avoid using such automatic catch-up for many/busy chats, as it can end up adding massive amounts of history-fetch requests on unstable network, and is not really how discord clients normally work (only display one screenful of a channel history that user is looking at). I.e. only set for chats where backlog is not just the usual background noise.
msg-history-fetch-limit option can adjust sanity-check limit on fetching such server-side backlogs, e.g. to avoid spamming IRC client with 1000s of irrelevant messages when reconnecting somewhere after a while.
[IRC bouncer]: https://ircv3.net/software/clients#bouncers [ZNC]: https://znc.in/ [Quassel]: https://www.quassel-irc.org/ [The Lounge]: https://thelounge.chat/
Discord user mentions and emojis
Mentions are @username tags on discord, designed to alert someone to direct-ish message.
With default config, when you see e.g. <Galaxy🌌·Brain> Hi! and want to reply highlighting them, sending Hey @galaxy and welcome should probably work. Can also use their full irc nick, to be sure.
How it works: if rdircd matches msg-mention-re regexp conf-option against something in a message being sent (e.g. @galaxy @-mention above), that'd be treated as a "mention", which is either uniquely-matched and translated into a discord mention in the sent message, or returns an error notice (with nicks that match that mention ambiguously, if any).
Default value for it should look like this:
ini
[discord]
msg-mention-re = (?:^|\s)(@)(?P<nick>[^\s,;@+!]+)
Which would match any word-like space- or punctuation-separated @nick mention in sent lines.
Regexp ([python "re" syntax]) must have named "nick" group with nick/username lookup string, which will be replaced by discord mention tag, and all other capturing groups (i.e. ones without ?:) will be stripped (like @ in above regexp).
Default regexp above should still allow to send e.g. \@something to appear non-highlighted in webapp (and without \ due to markdown), as it won't be matched by (?:^|\s) part due to that backslash prefix.
As another example, to have classic irc-style highlights at the start of the line, regexp like this one can be used:
msg-mention-re = ^(?P
And should translate e.g. mk-fg: some msg into @mk-fg some msg (with @-part being mention-tag). Trailing space is included in regexp there to avoid matching URL links.
To ID specific discord user, "nick" group will be used in following ways:
- Case-insensitive match against all recent guild-related irc names
user-mention-cache-timeout config option controls "recent" timeout.
- Lookup unique name completion by prefix, same as discord does in webui for
- If no cached or unique match found - error notice will be issued
Such strict behavior is designed to avoid any unintentional mis-translations, and highlighting wrong person should generally only be possible via misspelling.
Related msg-mention-re-ignore option (regexp to match against full capture of pattern above) can also be used to skip some non-mention things from being treated as such, that'd otherwise be picked-up by first regexp, stripping capturing groups from them too, which can be used to e.g. undo escaping.
Note that discord user lists can be quite massive (500K+ users), are not split by channel, and are not intended to be pre-fetched by the client, only queried for completions or visible parts, which doesn't map well to irc, hence all this magic.
Similar regexp is configured for per-discord emojis:
msg-emoji-re = (?:^|\s)(:)(?P
Where for example I use :Arch: btw from IRC will match that regexp, lookup/replace "emoji" group there using this discord's emojis (case-insensitive), and either send it translated as I use 🐧 btw, or return error notice if such emoji isn't available in that discord and not on a list of generic unicode ones.
Some emojis are global and auto-translated to unicode characters, listed in rdircd.unicode-emojis.txt.gz file, which is normally looked up next to rdircd script, but msg-emoji-unicode-list-file option allows to set different path or disable using it.
Set msg-mention-re / msg-emoji-re to an empty value to disable such translations.
editsdeletesforjust-sent_messages>
Quick edits/deletes for just-sent messages
Similar to [Discord user mentions] above, there's a special regexp-option that matches commands to be interpreted as edit or removal of last message sent to this channel.
Default regexps look something like this (check [--conf-dump-defaults] jic):
ini
[discord]
msg-edit-re = ^\ss(?P<sep>[/|:])(?P<aaa>.+?)(?P=sep)(?P<bbb>.?)(?P=sep)?\s*$
msg-del-re = ^\s//del\s$
They match sed/perl/irc-like follow-up amendment lines like s/spam/ham (closing character at the end is optional in default regexp), and //del line, which will never be sent to discord, only used as internal commands.
(s| /some/path | /other/path | and s:cat /dev/input/mouse0 | hexdump:hexdump </dev/input/mouse0: syntaxes are also allowed by default edit-regexp, just like with [sed], for easier handling of common stuff like paths, which can have these chars in them, and closing character allows including trailing spaces in replacement)
Both commands matched by these operate on last message sent by rdircd to the same discord channel, with //del simply removing that last message, and edit running [python re.sub()] ([PCRE-like]) regexp-replacement function on it.
"msg-edit-re" regexp option value matching sed-like command must have named "aaa" and "bbb" groups in it, which will be used as pattern and replacement args to re.sub(), respectively.
If edit doesn't seem to alter last-sent message in any way, it gets discarded, and also generates IRC notice response, to signal that replacement didn't work.
Successful edit/deletion should also be signaled as usual by discord, with \[edit\] or such prefix (configurable under \[irc\] section).
Any older-than-last messages can be edited through Discord WebUI - this client only tracks last one for easy quick follow-up oops-fixes, nothing more than that.
[Discord user mentions]: #hdr-discordusermentionsandemojis [--conf-dump-defaults]: rdircd.defaults.ini [sed]: https://en.wikipedia.org/wiki/Sed [python re.sub()]: https://docs.python.org/3/library/re.html#re.sub [PCRE-like]: https://en.wikipedia.org/wiki/PerlCompatibleRegular_Expressions
@silent messages and other such flags
Somewhat similar to quick edits/deletes above, "msg-flag-silent-re" option is there to match/remove "@silent" prefix in messages (by default), which disables sending discord push notifications for it, same as with the official client.
That and similar message flags on incoming messages are not represented in any way, as they don't seem to be relevant for an irc client anyway.
replacementsblocksinincoming_o.KGob>
Custom replacements/blocks in incoming/outgoing IRC messages
Config can have a \[recv-replacements\] and/or \[send-replacements\] sections to block IRC lines, or regexp-replace parts of those received or sent (by you), optionally on per-discord basis.
send-replacements section can be used to add discord-specific tags, unicode shorthands, emojis, stickers, block/replace specific links or maybe even words/language before proxying msg to discord.
Or for \[recv-replacements\], simple use-case can be to e.g. find @-mentions and transform those to <nick>: <msg> for IRC client, cleanup or sanitize messages from non-IRC-friendly noise.
Here's how send-replacements can look in the ini file(s):
ini
[send-replacements]
*.unicode-smiley = (^| ):\)( |$) -> \1😀\2 .twitter-to-nitter = ^(https?://)((mobile|www)\.)?twitter\.com(/.)?$ -> \1nitter.ir\4 *.tag-for-multiline-msgs = <br> -> \n
guildx.never-mention-rust! = (?i)\brust\b -> <block!> guildx.localize-color-word = \bcolor(ed|i\S+)\b -> colour\1
Where each key has the form of <discord-prefix>.<comment>, with a special * prefix to apply rule to all discords, while values are <regexp> -> <replacement-or-action> with one special <block!> action-value to block sending msg with error-notice on regexp match.
So when sending e.g. test :) msg on IRC, discord will get test 😀
<comment> part of the key can be any arbitrary unique string.\ \n in replacement values is turned into an actual newline character, so can allow to send multiline messages, e.g. by inserting <br> line-separators with rule from example above.
Replacements are applied in the same order as specified, but with * keys preceding per-discord ones, and before processing to add discord tags, so anything special like @username that can normally be typed in messages can be used there too.
recv-replacements work in the same way on received IRC message contents:
ini
[recv-replacements]
.prefix-highlight-my-nick = .@(user6943)\b.* -> \1: \0 *.skip-redundant-gif-info = ^\S+ Gifv (Tenor) :: -> <block!> *.replace-standalone-shit-word = \bshit\b -> crap
guildx.ignore-all-msg-edits-here = ^(\S+ :: )?\[edit\] -> <block!> gaming.discard-news-image-links-in-monitor = ^#gaming.news :: \S+ Image :: -> <block!>
Note that these replacements work on IRC-lines level, so for a multiline discord message they'd replace/remove specific matched line(s), if any. See "[Custom filtering for all received messages]" below to filter by rules matching entire discord message instead of specific IRC messages (lines) it's translated into.
Same as with other regex-using options, regexps have python "re" module syntax, applied via [re.sub()] function, using raw strings from config value as-is, without any special escapes or interpretations.
#rdircd.control channel has recv-repl and send-repl commands to edit these rules on-the-fly.
[re.sub()]: https://docs.python.org/3/library/re.html#re.sub
filteringforallreceived_messages>
Custom filtering for all received messages
If you join #rdircd.monitor channel, see - for example - a message like this:
...and think "don't want to see messages like that again!" - config files' \[recv-regexp-filters\] section or corresponding rx command in #rdircd.control channel can help.
Depending on what "messages like that" means, here are some ways to filter those out:
ini
[recv-regexp-filters]
discard msgs from this bot = ^<helper-bot>
ignore all msgs in that channel of that discord = ^\S+ #pub\.welcomes ::
drop all msgs from "pub" discord = ^\S+ #pub\.
no messages from #welcomes channels of any discord pls = ^\S+ #\w+\.welcomes ::
never see "Welcome!" message-text again!!! = ^\S+ #\S+ :: Welcome!$
some combination of the above = (?i)^<helper-bot> #\w+\.welcomes ::
...
(tweak e.g. [last example on regex101.com] for more hands-on understanding)
Lines in that section have the usual <key> = <regexp> form, where <key> part can be anything (e.g. comment to explain regexp, like in examples above), and <regexp> value is a regular expression to match against the message in <user> #discord.channel-name :: message text format like that helper-bot msg presented above, and similar as it can be seen in monitor-channels.
Any message received from discord will be matched against all regexps in order, stopping and discarding the message everywhere on first (any) match. So it might be a good idea to write as precise patterns as possible, to avoid them matching anything else and dropping unrelated messages accidentally. Also note that full discord message is matched here, not specific IRC-lines of it, so regexp matching any part of a muliline message will block the whole thing.
Same as with some other conf options, basic knowledge of regular expressions might be needed to use such filters - [here's a link to nice tutorial on those] (though there are 100s of such tutorials on the web).
Particular regexps here use PCRE-like [python re syntax], with re.DOTALL flag set (. matches newlines in multiline messages). I'd also recommend commonly adding (?i) case-insensitive-match flag, as IRC nicks and channel names ignore character case and can be displayed in misleading/inprecise ways in the client.
More random examples of \[recv-regexp-filters\], incl. more advanced CNF weirdness:
ini
[recv-regexp-filters]
disregard wordle thread there = ^\S+ #pub\.general\.=w8mk\.wordle ::
ignore "wordle" threads everywhere = ^\S+ #\S+\.=\w{4}\.wordle ::
activity-level bots are annoying = (?i) advanced to level \d+[ !]
gif replies of YY in ZZ = (?i)^<YY> #ZZ\.\S+ :: (-- re:[^\n]+\n)?\[att\] .*/image\d\.gif\?
;; Advanced stuff: connect multiple regexps via CNF logic (Conjunctive Normal Form) ;; If key starts with "∧ " (conjunction symbol), it's AND'ed with previous regexp ;; ¬ (negation) in that prefix inverts the logic, so e.g. "∧¬ ..." is "and not ..." ;; Disjunction (∨) is the default behavior and doesn't need the (implied) prefix ;; Any complex logical expression can be converted to such CNF form - ;; - use calculators like https://www.dcode.fr/boolean-expressions-calculator
Drop welcome msgs in welcome-chans = (?i)^\S+ #\w+\.\Swelcome\S :: .\bwelcome\b. ∧ but only if they have an exclaimation mark in them somewhere = :: .*! ∧¬ and not from this specific "lut" discord-prefix = ^\S+ #lut\.
Most channels here are not relevant = ^\S+ #myc\. ∧¬ except these ones = ^\S+ #myc\.(announcements|changelog|forum)[. ] ∨ but skip github CI logs there = ^<github> #myc\.
Pretty much anything can be matched with clever regexps, so CNF-logic stuff like in last examples is seldom useful, but might still be simpler than expressing arbitrary ordering or negation in regexps.
See also match-counters config option to keep track of whether specific rule(s) are still needed/being-used.
[last example on regex101.com]: https://regex101.com/r/VMvyfS/2 [here's a link to nice tutorial on those]: https://github.com/ziishaned/learn-regex [python re syntax]: https://docs.python.org/3/howto/regex.html
Lookup discord IDs
Mostly useful for debugging - /who command can resolve specified ID (e.g. channel_id from protocol logs) to a channel/user/guild info:
/who ~#123456- find/describe channel with id=123456./who ~%123456- server/guild id info./who ~@123456- user id lookup.
/who ~@John·Mastodon- user IRC nick or name/login lookup.
/who ~#minecraft.general- info on discord channel, same as/t infothere.
/who ~*665560022562111489- translate discord snowflake-id to date/time.
In irc channels corresponding to ones on discord, /topic info command (often works as shortened /t info in clients too) can be used to print more information about linked discord channel and its server/guild.
/t info <username> can also print info on user in that discord (unlike /who ~@<username> which looks the name up in all connected discords), for example /t info john will print info for anyone with "john" in the name.
Usernames in these queries can match exact irc name or discord username, in which case that result is returned, or otherwise more general server-side lookup is made, which can return matches in any type of discord name(s) (see [People's names on discord] for more info on those).
Channel name disambiguation
Discord name translation is "mostly" deterministic due to one exception - channels with same (casemapped) IRC name within same server/guild, which discord allows for.
When there is a conflict, chan names are suffixed by .<id-hash> (see chan-dedup-\* config options), to allow using both channels through IRC. Renaming conflicting channels on Discord will rename IRC chans to remove no-longer-necessary suffixes as well. Such renames affect thread-channels too.
Note that when channels a
README truncated. View on GitHub