2021-08-23 09:26:31 +00:00
# Fedi Groups
2021-08-28 08:42:27 +00:00
2021-08-23 09:26:31 +00:00
## How it works
This is an approximation of groups you can use right now with existing fedi software that implements the Mastodon client API.
A group is a regular user account controlled by a "bot" service that runs on a Linux server (e.g. a Rpi in your closet). To join a group, simply follow the group user. To post into the group, tag the group user in a top-level post (not a reply), or use `/b` in a reply to share the parent post to the group. The group will reblog shared posts to its members.
Groups implement moderation (banning users and instances, member-only mode with user whitelist). This is controlled by slash commands, see below.
Group admins can issue group announcements that are posted poublicly by the group user, such as when there is a planned maintenance. The group will attempt to catch up with posts missed during the outage.
2021-08-28 08:42:27 +00:00
*Note: In this document, "reblog" and "boost" are used interchangeably.*
2021-08-23 09:26:31 +00:00
### Advantages of emulated groups
Unlike some other attempts at group implementation (namely gup.pe or the mythical WIP Pleroma Groups), this works with current Pleroma and Mastodon. There's no need for interoperability in different server implementations, since it uses existing follow/mention/reblog features that are already cross-compatible. Mastodon users can join a group running on Pleroma and vice-versa.
Groups probably can't be hosted on Honk and Misskey, but their users can use groups hosted elsewhere just fine.
2021-08-22 11:01:33 +00:00
2021-08-26 21:18:24 +00:00
## Setup
2021-08-22 11:01:33 +00:00
### Building
Install the rust toolchain using `rustup` : [https://www.rust-lang.org/tools/install ](https://www.rust-lang.org/tools/install )
Rust 1.54+ is recommended.
Build with `cargo build` , or optimized: `cargo build --release` . The binary is placed in the `target/debug` or `target/release` directory.
You can also run the program using Cargo, that is handy for development: `cargo run` . When passing command line flags, use `--` : `cargo run -- -i my@group.xyz` .
### Setting up a group account
1. Create the group's account all nice like you want it. Pleroma and Mastodon should work, others may work too but you're on your own.
2. Run the group service with `fedigroups -a mygroup@groups.social` (your account's handle) to authenticate.
3. **Make sure you auth as the correct user!**
4. Paste the Oauth2 token you got into the terminal, hit enter.
2021-10-05 23:09:20 +00:00
The program now ends. The credentials are saved in the directory `groups/account@server/` , which is created if missing.
2021-08-22 11:01:33 +00:00
You can repeat this for any number of groups.
2021-08-26 21:18:24 +00:00
In case you need to re-authenticate an existing group, do the same but use `-A` instead of `-a` .
2021-08-22 11:01:33 +00:00
### Editing config
2021-10-11 22:20:42 +00:00
**Staring v0.4.1, the config files support comments!**
2021-10-04 22:44:39 +00:00
2021-10-05 08:39:10 +00:00
A typical setup could look like this:
2021-10-04 22:44:39 +00:00
2021-10-05 08:39:10 +00:00
```
2021-10-05 23:09:20 +00:00
├── groups
2021-10-05 08:39:10 +00:00
│ ├── betty@piggo.space
│ │ ├── config.json
│ │ ├── control.json
│ │ └── state.json
│ └── chatterbox@botsin.space
2021-10-11 11:24:17 +00:00
│ ├── config.json ... fixed config edited manually
│ ├── messages.json ... custom locale overrides (optional)
│ ├── control.json ... mutable config updated by the group service
│ └── state.json ... group state data
├── locales
│ ├── ... custom locale files, same format like en.json
│ └── ru.json
2021-10-05 08:39:10 +00:00
└── groups.json
```
2021-10-11 11:24:17 +00:00
#### Locales
English locale ("en") is bundled in the binary. Additional locales can be placed in the `locales` folder.
If an entry is missing, the English version will be used.
The locale file looks like this (excerpt):
```json
{
"group_announcement": "**📢Group announcement**\n{message}",
"ping_response": "pong, this is fedigroups service v{version}"
}
```
2021-10-11 22:12:19 +00:00
- All messages can use markdown formatting.
2021-10-11 11:24:17 +00:00
- Words in curly braces (`{}`) are substitution tokens. These must be preserved in all translations.
- Pay attention to line endings and blank lines (`\n`). Some messages from the locale file are combined to form the
final post, leaving out newlines can result in a mangled output.
The locale to use is chosen in each group's `config.json` , "en" by default (if not specified).
Group-specific overrides are also possible: create a file `messages.json` in the group folder
and define messages you wish to change, e.g. the greeting or announcement templates.
2021-10-05 08:39:10 +00:00
#### Common config
There is one shared config file: `groups.json`
2021-10-04 22:44:39 +00:00
2021-10-11 11:24:17 +00:00
- If the file does not exist, default settings are used. This is usually sufficient.
- This file applies to all groups and serves as the default config.
2021-10-05 08:39:10 +00:00
```
{
2021-10-11 11:24:17 +00:00
// name of the directory with groups
"groups_dir": "groups",
// name of the directory with locales
"locales_dir": "locales",
// Show warning if locales are missing keys
"validate_locales": true,
2021-10-05 08:39:10 +00:00
// Max number of missed notifs to process after connect
2021-10-11 11:24:17 +00:00
"max_catchup_notifs": 30,
2021-10-05 08:39:10 +00:00
// Max number of missed statuses to process after connect
2021-10-11 11:24:17 +00:00
"max_catchup_statuses": 50,
2021-10-05 08:39:10 +00:00
// Delay between fetched pages when catching up
2021-10-11 11:24:17 +00:00
"delay_fetch_page_s": 0.25,
2021-10-05 08:39:10 +00:00
// Delay after sending a status, making a follow or some other action.
// Set if there are Throttled errors and you need to slow the service down.
2021-10-11 11:24:17 +00:00
"delay_after_post_s": 0.0,
2021-10-05 08:39:10 +00:00
// Delay before trying to re-connect after the server closed the socket
2021-10-11 11:24:17 +00:00
"delay_reopen_closed_s": 0.5,
2021-10-05 08:39:10 +00:00
// Delay before trying to re-connect after an error
2021-10-11 11:24:17 +00:00
"delay_reopen_error_s": 5.0,
2021-10-05 08:39:10 +00:00
// Timeout for a notification/timeline socket to be considered alive.
// If nothing arrives in this interval, reopen it. Some servers have a buggy socket
// implementation where it stays open but no longer works.
2021-10-11 11:24:17 +00:00
"socket_alive_timeout_s": 30.0,
2021-10-05 08:39:10 +00:00
// Time after which a socket is always closed, even if seemingly alive.
// This is a work-around for servers that stop sending notifs after a while.
2021-10-11 11:24:17 +00:00
"socket_retire_time_s": 120.0
2021-10-05 08:39:10 +00:00
}
```
#### Per-group config
2021-10-05 23:09:20 +00:00
Each group is stored as a sub-directory of `groups/` . The sub-directories are normally named after their accounts,
but this is not required. For example, `groups/betty@piggo.space/` .
2021-10-05 08:39:10 +00:00
The group's config and state is split into three files in a way that minimizes the risk of data loss.
Only the `config.json` file with credentials is required; the others will be created as needed by the group daemon.
- `config.json` - immutable config, never changed beside when you run the `-A` command to reauth a group.
This is where the account name, the auth token and the `enabled` flag are stored.
- `control.json` - settings and state that change at runtime or can be set using slash commands.
2021-10-04 22:44:39 +00:00
This file is overwritten by the group service when needed.
- `state.json` - frequently changing state data. The last-seen status/notification timestamps are kept here.
2021-10-05 08:39:10 +00:00
State is split from Control to limit the write frequency of the control file. Timestamps can be updated multiple times
2021-10-04 22:44:39 +00:00
per minute.
2021-10-11 11:24:17 +00:00
- `messages.json` - optional per-group locale overrides
2021-10-04 22:44:39 +00:00
2021-10-05 08:39:10 +00:00
**Do not edit the control and state files while the group service is running, it may overwrite your changes!**
The JSON files are easily editable, you can e.g. add yourself as an admin (use the e-mail format, e.g. `piggo@piggo.space` ).
Note that changing config externally requires a restart. It's better to use slash commands and update the config at run-time.
2021-10-04 22:44:39 +00:00
2021-10-05 08:39:10 +00:00
When adding hashtags, *they must be entered as lowercase* and without the `#` symbol!
2021-08-22 11:01:33 +00:00
2021-10-11 22:20:42 +00:00
The file formats are quite self-explanatory:
2021-08-22 11:01:33 +00:00
2021-10-05 08:39:10 +00:00
**config.json**
2021-10-04 22:44:39 +00:00
2021-08-22 11:01:33 +00:00
```json
{
2021-10-05 08:39:10 +00:00
// Enable or disable the group service
2021-10-04 22:44:39 +00:00
"enabled": true,
2021-10-11 11:24:17 +00:00
// Group locale (optional, defaults to "en")
"locale": "en",
2021-10-05 08:39:10 +00:00
// Group account name
2021-10-04 22:44:39 +00:00
"acct": "group@myserver.xyz",
2021-10-11 11:24:17 +00:00
// Saved mastodon API credentials, this is created when authenticating the group.
2021-10-04 22:44:39 +00:00
"appdata": {
"base": "https://myserver.xyz",
"client_id": "...",
"client_secret": "...",
"redirect": "urn:ietf:wg:oauth:2.0:oob",
"token": "..."
2021-08-22 11:01:33 +00:00
}
}
```
2021-10-05 08:39:10 +00:00
**control.json**
2021-10-04 22:44:39 +00:00
```json
{
2021-10-05 08:39:10 +00:00
// List of group hashtags, lowercase.
// The group reblogs anything with these hashtags if the author is a member.
2021-10-04 22:44:39 +00:00
"group_tags": [
"grouptest"
],
2021-10-05 08:39:10 +00:00
// List of admin users (e-mail format)
2021-10-04 22:44:39 +00:00
"admin_users": [
"admin@myserver.xyz"
],
2021-10-05 08:39:10 +00:00
// Restrict write access to manually added members
2021-10-04 22:44:39 +00:00
"member_only": false,
2021-10-05 08:39:10 +00:00
// List of member users (e-mail format)
2021-10-04 22:44:39 +00:00
"member_users": [],
2021-10-05 08:39:10 +00:00
// List of banned users (e-mail format), their posts and actions are ignored by the group
2021-10-04 22:44:39 +00:00
"banned_users": [],
2021-10-05 08:39:10 +00:00
// List of banned servers, users from there can't interact with the group and their posts can't be shared.
2021-10-04 22:44:39 +00:00
"banned_servers": [
"bad-stuff-here.cc"
]
}
```
2021-10-05 08:39:10 +00:00
**state.json**
2021-08-22 11:01:33 +00:00
2021-10-05 08:39:10 +00:00
Internal use, millisecond timestamps of the last-seen status and notification.
2021-10-04 22:44:39 +00:00
```json
{
"last_notif_ts": 1630011219000,
"last_status_ts": 1630011362000
}
```
2021-08-22 11:01:33 +00:00
### Running
2021-10-05 08:39:10 +00:00
To run the group service, simply run it with no arguments.
2021-10-05 23:09:20 +00:00
It will read the `groups.json` file (if present), find groups in `groups/` and start the services for you.
2021-08-22 11:01:33 +00:00
2021-10-05 08:39:10 +00:00
Note that the control and status files must be writable, they are updated at run-time.
Config files can have limited permissions to avoid accidental overwrite.
2021-08-22 11:01:33 +00:00
2021-10-05 08:39:10 +00:00
An example systemd service file is included in the repository as well.
Make sure to set up the system user/group and file permissions according to your needs.
You can use targets in the included `Makefile` to manage the systemd service and look at logs.
2021-08-26 21:18:24 +00:00
## Group usage
2021-08-23 11:35:03 +00:00
2021-08-28 08:24:35 +00:00
### Sharing into the group
2021-08-28 08:42:27 +00:00
The group will boost any status meeting these criteria:
- The visiblity is public or unlisted
- It's not a command request (i.e. mentions the group user and contains valid command(s))
- Either:
- it mentions the group user and is not a reply
- or, it contains one of the group hashtags
Examples of posts that will be shared:
- `@group Look at this duck` (public or unlisted)
- `Look at this duck @group` (public or unlisted)
- `I love #ducks` (public or unlisted, if #ducks is a group hashtag)
- `@otheruser tell me about #ducks` (in a thread, public or unlisted, if #ducks is a group hashtag)
- `Ducks are cool @otheruser @group` (original post)
These won't be shared:
- `ducks suck`
2021-08-28 08:43:39 +00:00
- `@group #ducks /i` (anything with the "ignore" command is ignored)
2021-08-28 08:42:27 +00:00
- `@group /remove #ducks` (admin command, even if it includes a group hashtag)
- `@otheruser @group tell me about ducks` (in a thread)
2021-08-28 08:24:35 +00:00
2021-08-22 11:01:33 +00:00
### Commands
Commands are simple text lines you use when mentioning the group user. DMs work well for this.
One post can contain multiple commands; the replies will be batched to one response.
2021-08-26 21:18:24 +00:00
Replies keep the same visibility level as the post with the command.
2021-08-23 09:26:31 +00:00
2021-08-26 21:18:24 +00:00
Posts with commands, mentioning the group, won’ t be boosted.
### Membership
When a user follows a group, the group follows them back and marks them internally as a member. (In member-only groups, a group admin must initiate the second part).
Admin can add or remove group members using the `/add` and `/remove` commands. Users can use `/join` and `/leave` .
Members can use `/join` to make the group re-follow them, for example when the follow somehow stopped working.
2021-08-22 11:01:33 +00:00
2021-08-26 21:18:24 +00:00
### Group mentions
Any user (member in member-only groups) can post to the group by mentioning the group user. The post is then reblogged.
### Group hashtags
2021-08-26 22:19:43 +00:00
Admins can add hashtags to the group config (`/add #hashtag `, remove the same way: ` /remove #hashtag `). Hashtags are case-insensitive.
2021-08-26 21:18:24 +00:00
When a *group member* posts one of the group hashtags, the group will reblog it. This is a nicer way to share posts, you don't have to mention the group user at all.
For group hashtags to work, the group user must follow all its members; otherwise the posts might not federate to the group's server.
2021-08-30 18:03:03 +00:00
### Opting-out and #nobot
The group service respects the `#nobot` tag in users' profiles. When it's detected, the user's posts can't be shared to the group using the `/boost` command, unless they explicitly join.
To prevent individual groups from boosting your posts, use the `/optout` command.
2021-08-26 21:18:24 +00:00
### List of commands
*Note on command arguments:*
- When a command wants user handle as an argument, use the e-mail form, e.g. piggo@piggo.space, to avoid mentioning the user.
- Local users can be specified without the domain part, but the first `@` is then needed.
- When specifying a server (for server ban commands), the name must contain at least one dot.
- Hashtags are specified in their full form with a hash.
**Basic commands**
- `/help` - show help
2021-08-28 08:24:35 +00:00
- `/ignore` (alias `/i` ) - make the group completely ignore the post
- `/members` (alias `/who` ) - show group members / admins
2021-08-26 21:18:24 +00:00
- `/tags` - show group hashtags
2021-08-28 08:24:35 +00:00
- `/boost` (alias `/b` ) - boost the replied-to post into the group
2021-08-26 21:18:24 +00:00
- `/ping` - ping the group service to check it's running, it will reply
- `/join` - join the group
2021-08-22 11:01:33 +00:00
- `/leave` - leave the group
2021-08-30 18:03:03 +00:00
- `/optout` - forbid sharing of your posts to the group (no effect for admins and members)
- `/optin` - reverse an opt-out
2021-08-27 19:52:33 +00:00
- `/undo` - undo a boost of your post into the group, e.g. when you triggered it unintentionally. Use in a reply to the boosted post, tagging the group user. You can also un-boost your status when someone else shared it into the group using `/boost` , this works even if you're not a member.
2021-08-22 11:01:33 +00:00
**For admins**
2021-08-26 21:18:24 +00:00
- `/announce x` - make a public announcement from the rest of the status. Note: this does not preserve any formatting!
2021-08-28 08:24:35 +00:00
- `/ban user@domain` - ban a user from interacting with the group or having their statuses shared
- `/unban user@domain` - lift a user ban
- `/ban domain.tld` - ban a server (works similar to instance mute)
- `/unban domain.tld` - lift a server ban
- `/op user@domain` (alias `/admin` ) - grant admin rights to a user
- `/deop user@domain` (alias `/deadmin` ) - revoke admin rights
2021-08-30 17:10:03 +00:00
- `/closegroup` - make the group member-only
- `/opengroup` - make the group public-access
2021-08-28 08:24:35 +00:00
- `/add user@domain` (alias `/follow` ) - add a member
- `/remove user@domain` (alias `/remove` ) - remove a member
- `/add #hashtag` (alias `/follow` ) - add a hashtag to the group
- `/remove #hashtag` (alias `/unfollow` ) - remove a hashtag from the group
- `/undo` (alias `/delete` ) - when used by an admin, this command can un-boost any status. It can also delete an announcement made in error.