Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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