Skip to content

Commit

Permalink
Merge pull request #2 from v411e/develop
Browse files Browse the repository at this point in the history
Fixes and features from tante

- Fix Dockerfile not creating all necessary directories
- Split up config into `config.yaml` and `auth.yaml`
- Add feature to filter instances
  • Loading branch information
v411e authored Oct 13, 2023
2 parents 1ec1fcb + ed30ff4 commit 358cc3c
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 42 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ RUN pip install -r requirements.txt

VOLUME /app/config

COPY ./config /app
RUN mkdir -p /app/config/
RUN mkdir -p /app/secrets/

COPY ./config/* /app/config/

ENTRYPOINT ["python", "/app/main.py"]
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
![](./res/hype_header.png)

# hype

This Mastodon bot transfers the trends from other instances directly to your personal timeline. You decide which instances it fetches and how much you want to see per instance.

## Why
I am hosting my own mastodon instance and my server is very small (~2 active users). This is why trends simply do not work on my instance. There is just not enough activity. I used to open up the explore-pages of other interesting mastodon instances once per day to discover interesting topics and posts beyond my subscriptions. But that is a bit tedious in the long run. One afternoon I decided to write a bot for this issue and here we are :tada:

For smaller instances the local timeline is rather empty. This is why trends simply do not work on those instances: There is just not enough activity. Instead of manually checking out other instances this bot allows to subscribe to a multitude of different mastodon compatible servers to fetch trending posts and repost them to your current server helping discoverability of accounts, people and topics within the federated social web.

## Installation

Deploy with docker-compose

```yaml
Expand All @@ -17,23 +21,35 @@ services:
- ./config:/app/config
```
## Configuration
Create a `config.yaml` file in `./config/` and enter the credentials of your bot-account. Also define how often the bot should run. See the example below:
Create a `config.yaml` and a `auth.yaml` file in `./config/`. Enter the credentials of your bot-account into `auth.yaml`. You can define which servers to follow and how often to fetch new posts as well as how to automatically change your profile in config.yaml. See the examples below:

`auth.yaml`:

```yaml
# Credentials for your bot account
bot_account:
server: "mastodon.example.com"
email: "[email protected]"
password: "averylongandsecurepassword"
```

`config.yaml`

```yaml
# Refresh interval in minutes
interval: 60
# Define subscribed instances and
# Text to add to the bot profile befor the list of subscribed servers
profile_prefix: "I am boosting trending posts from:"
# profile fields to fill in
fields:
code: https://github.com/tante/hype
operator: "YOUR HANDLE HERE"
# Define subscribed instances and
# their individual limit (top n trending posts)
# which is again limited by the API to max 20
subscribed_instances:
Expand All @@ -49,5 +65,3 @@ subscribed_instances:
- Update bot profile with list of subscribed instances

---

<a rel="me" href="https://mastodon.keks.club/@hype">Hype on Mastodon</a>
5 changes: 5 additions & 0 deletions config/auth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Credentials for your bot account
bot_account:
server: ""
email: ""
password: ""
29 changes: 20 additions & 9 deletions config/config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
# Credentials for your bot account
bot_account:
server: ""
email: ""
password: ""

# Refresh interval in minutes
interval: 60

# Define subscribed instances and
# Text to add to the bot profile befor the list of subscribed servers
profile_prefix: "I am boosting trending posts from:"

# profile fields to fill in
fields:
code: https://github.com/v411e/hype
operator: "YOUR HANDLE HERE"

# Define subscribed instances and
# their individual limit (top n trending posts)
# which is again limited by the API to max 20
subscribed_instances:
chaos.social:
limit: 20
limit: 5
mastodon.social:
limit: 5
limit: 5

# Posts originating from filtered instances will never be reposted.
# The filter checks for the instance of the original posting account, not the
# server that marked it as a popular post.
# This can be used to filter out abusive instances as well as protect small
# instances who could be overwhelmed with a repost to a significant amount of
# other instances
filtered_instances:
- example.com
72 changes: 53 additions & 19 deletions hype/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List
import yaml, logging
import yaml
import logging


class BotAccount:
Expand Down Expand Up @@ -33,13 +34,20 @@ class Config:
interval: int = 60 # minutes
log_level: str = "INFO"
subscribed_instances: List = []
filtered_instances: List = []
profile_prefix: str = ""
fields: dict = {}

def __init__(self):
path = "/app/config/config.yaml"
# path = "../config/config-prod.yaml"
with open(path, "r") as configfile:
# auth file containing login info
auth = "/app/config/auth.yaml"
# settings file containing subscriptions
conf = "/app/config/config.yaml"

# only load auth info
with open(auth, "r") as configfile:
config = yaml.load(configfile, Loader=yaml.Loader)
logging.getLogger("Config").debug("Load config")
logging.getLogger("Config").debug("Loading auth info")
if (
config
and config.get("bot_account")
Expand All @@ -55,20 +63,46 @@ def __init__(self):
else:
logging.getLogger("Config").error(config)
raise ConfigException("Bot account config is incomplete or missing.")
self.interval = (
config["interval"] if config.get("interval") else self.interval
)
self.log_level = (
config["log_level"] if config.get("log_level") else self.log_level
)
self.subscribed_instances = (
[
Instance(name, props["limit"])
for name, props in config["subscribed_instances"].items()
]
if config.get("subscribed_instances")
else []
)

with open(conf, "r") as configfile:
config = yaml.load(configfile, Loader=yaml.Loader)
logging.getLogger("Config").debug("Loading settings")
if config:
self.interval = (
config["interval"] if config.get("interval") else self.interval
)
self.log_level = (
config["log_level"] if config.get("log_level") else self.log_level
)

self.profile_prefix = (
config["profile_prefix"]
if config.get("profile_prefix")
else self.profile_prefix
)

self.fields = (
{name: value for name, value in config["fields"].items()}
if config.get("fields")
else {}
)

self.subscribed_instances = (
[
Instance(name, props["limit"])
for name, props in config["subscribed_instances"].items()
]
if config.get("subscribed_instances")
else []
)

self.filtered_instances = (
[
name for name in config["filtered_instances"]
]
if config.get("filtered_instances")
else []
)


class ConfigException(Exception):
Expand Down
18 changes: 12 additions & 6 deletions hype/hype.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import time, schedule, time, logging
import time
import schedule
import logging
from mastodon import Mastodon
from config import Config
import os.path
Expand Down Expand Up @@ -29,10 +31,10 @@ def update_profile(self):
subscribed_instances_list = "\n".join(
[f"- {instance}" for instance in self.config.subscribed_instances]
)
note = f"""I am boosting trending posts from:
note = f"""{self.config.profile_prefix}
{subscribed_instances_list}
"""
fields = [("Github", "https://github.com/v411e/hype")]
fields = [(key, value) for key, value in self.config.fields.items()]
self.client.account_update_credentials(
note=note, bot=True, discoverable=True, fields=fields
)
Expand All @@ -50,18 +52,22 @@ def boost(self):
counter = 0
for trending_status in trending_statuses:
counter += 1
# Get snowflake-id of status on the instance where the status will be boosted
# Get snowflake-id of status on the instance where the status will be boosted # noqa: E501
status = self.client.search_v2(
trending_status["uri"], result_type="statuses"
)["statuses"]
if len(status) > 0:
status = status[0]
# check if post comes from a filtered instance
source_account = status["account"]["acct"].split("@")
server = source_account[-1]
filtered = server in self.config.filtered_instances
# Boost if not already boosted
already_boosted = status["reblogged"]
if not already_boosted:
if not already_boosted and not filtered:
self.client.status_reblog(status)
self.log.info(
f"{instance.name}: {counter}/{len(trending_statuses)} {'ignore' if already_boosted else 'boost'}"
f"{instance.name}: {counter}/{len(trending_statuses)} {'ignore' if (already_boosted or filtered) else 'boost'}"
)
else:
self.log.warning(
Expand Down

0 comments on commit 358cc3c

Please sign in to comment.