Compare commits

...

6 Commits
main ... spc

Author SHA1 Message Date
Moon Man 60c1ace489 tidy db resource close 2024-12-16 15:24:49 +00:00
Moon Man 106a487dc5 handle errored user accounts 2024-12-16 14:48:10 +00:00
Moon Man 72eac07711 now use sqlite for who posted before. 2024-12-16 13:56:43 +00:00
Moon Man 64a5d0c040 rm old temp file 2024-12-16 13:01:29 +00:00
Moon Man b5d430ed37 many improvements 2024-12-16 12:59:26 +00:00
Moon Man f525310f1c many changes for spc 2024-12-14 23:42:47 +00:00
9 changed files with 179 additions and 43 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
__pycache__ __pycache__
list list
token token
./*.png
.env
feditree/feditree.png
bots.db

View File

@ -1,17 +1,17 @@
# Bots de masto.es # SPC fork of Bots from masto.es
Aquí se encuentra el código de los bots que se utilizan en masto.es para ayudar con tareas de administración como guiar a los nuevos usuarios, ofrecer funciones adicionales o simplemente servir de pasatiempo. ## Installation
## Apreciabot Create a venv and inside of it run:
Envía un mensaje directo (visibilidad "sólo cuentas mencionadas") a @apreciabot@masto.es incluyendo el usuario que quieres apreciar con el formato "usuario@servidor" (excluyendo el primer "@" para evitar mencionarlo). Añade "croqueta" al final para activar el modo croqueta.
Basado en el Niceness Bot https://botsin.space/@nicenessbot pip install -r requirements.txt
## Bienvenibot ## Running feditree
Revisa las notificaciones de un usuario con los privilegios suficientes para buscar nuevos registros y envía un mensaje de bienvenida.
Create a '.env' file containing:
## Requisitos FEDI_DOMAIN=your-domain.tld
- Python 3
- Mastodon.py https://github.com/halcy/Mastodon.py Create a file 'token/faggotree' containing your bearer token value
- Apreciabot: bs4
then just activate your venv or run python from your venv, with 'feditree.py'

View File

@ -1,3 +1,4 @@
import sqlite3
from mastodon import Mastodon from mastodon import Mastodon
from sys import exit from sys import exit
@ -39,6 +40,84 @@ def list_append(name, value):
file.write(value + '\n') file.write(value + '\n')
file.close() file.close()
conn = sqlite3.connect("bots.db", isolation_level=None)
cursor = conn.cursor()
cursor.executescript('''
create table if not exists bots (
id integer primary key autoincrement,
name text not null
);
create table if not exists posted_already (
id integer primary key autoincrement,
bot_id integer not null,
user_id text not null,
created timestamp default current_timestamp,
unique(bot_id, user_id),
foreign key (bot_id) references bots(id)
);
''')
def get_or_create_bot(name):
cursor.execute("SELECT id FROM bots WHERE name = ?", (name,))
result = cursor.fetchone()
if result:
return result[0]
cursor.execute("INSERT INTO bots (name) VALUES (?)", (name,))
return cursor.lastrowid
def has_user_posted(bot_name, user_id):
user_id = str(user_id)
bot_id = get_or_create_bot(bot_name)
cursor.execute("""
SELECT EXISTS(
SELECT 1
FROM posted_already
WHERE bot_id = ? AND user_id = ?
LIMIT 1
)
""", (bot_id, user_id))
return bool(cursor.fetchone()[0])
def mark_user_posted(bot_name, user_id):
user_id = str(user_id)
bot_id = get_or_create_bot(bot_name)
try:
cursor.execute("""
INSERT INTO posted_already (bot_id, user_id)
VALUES (?, ?)
""", (bot_id, user_id))
return True
except sqlite3.IntegrityError:
return False
def unmark_user_posted(bot_name, user_id):
user_id = str(user_id)
bot_id = get_or_create_bot(bot_name)
cursor.execute("""
DELETE FROM posted_already
WHERE bot_id = ? AND user_id = ?
""", (bot_id, user_id))
return cursor.rowcount > 0 # Returns True if a row was deleted
def db_close():
try:
cursor.close()
conn.close()
except Exception:
pass
# It is not safe to get notifications from "last_id" because some may have been deleted # It is not safe to get notifications from "last_id" because some may have been deleted
def get_new_notifications(api, bot_name, types=None): def get_new_notifications(api, bot_name, types=None):
last_notifications=list_read(bot_name + '_last_notifications') last_notifications=list_read(bot_name + '_last_notifications')

4
faggotree.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
cd /var/lib/bots/vcs/int/mastoes-bots
/var/lib/bots/venvs/faggotree/bin/python3 feditree.py

View File

@ -1,8 +1,19 @@
from PIL import Image
from common import get_api, get_new_notifications, list_read, list_append from dotenv import load_dotenv
from PIL import Image, ImageFile
from common import get_api, get_new_notifications, has_user_posted, mark_user_posted, unmark_user_posted, db_close
import datetime import datetime
import gettext import gettext
import requests import requests
import os
import sys
import tempfile
ImageFile.LOAD_TRUNCATED_IMAGES = True
load_dotenv()
bot_name = "faggotree"
coordinates = [ coordinates = [
(350,200), (350,200),
@ -17,6 +28,8 @@ coordinates = [
(575,650), (575,650),
] ]
temp_image_name = (tempfile.NamedTemporaryFile(suffix='.png', delete=False)).name
def get_ordered_accounts_ids(account_id): def get_ordered_accounts_ids(account_id):
current_year = datetime.date.today().year current_year = datetime.date.today().year
accounts = {} accounts = {}
@ -24,16 +37,19 @@ def get_ordered_accounts_ids(account_id):
max_id=None max_id=None
loops = 0 loops = 0
while stop == False: while stop == False:
statuses = api.account_statuses(account_id, exclude_reblogs=True, max_id=max_id, limit=80) statuses = api.account_statuses(account_id, exclude_reblogs=True, max_id=max_id, limit=50)
for status in statuses: for status in statuses:
if(status.created_at.year < current_year): if(status.created_at.year < current_year):
stop = True stop = True
break break
for mention in status.mentions: for mention in status.mentions:
if mention.id not in accounts: if mention.username == bot_name or mention.username == status.account.username:
accounts[mention.id] = 1 pass
else: else:
accounts[mention.id] = accounts[mention.id] + 1 if mention.id not in accounts:
accounts[mention.id] = 1
else:
accounts[mention.id] = accounts[mention.id] + 1
max_id=status.id max_id=status.id
loops = loops + 1 loops = loops + 1
if loops > 10 : if loops > 10 :
@ -46,13 +62,25 @@ def get_ordered_accounts_ids(account_id):
def get_accounts(accounts_ids): def get_accounts(accounts_ids):
accounts = [] accounts = []
for i in range(len(accounts_ids)): for i in range(len(accounts_ids)):
account = api.account(accounts_ids[i]) try:
if "nobot" not in account.note: account = api.account(accounts_ids[i])
accounts.append(account) accounts.append(account)
if len(accounts) == len(coordinates): if len(accounts) == len(coordinates):
break break
except Exception as e:
print(e)
print("Failed to get account id: " + accounts_ids[i])
return accounts return accounts
def get_avatar():
try:
with Image.open(temp_image_name) as avatar:
avatar.load()
return avatar
except:
with Image.open('feditree/default.png') as avatar:
avatar.load()
return avatar
def create_image(accounts): def create_image(accounts):
with Image.open("feditree/fediverse-christmas-tree.png") as feditree: with Image.open("feditree/fediverse-christmas-tree.png") as feditree:
@ -65,14 +93,14 @@ def create_image(accounts):
account = api.account(accounts[i]) account = api.account(accounts[i])
avatar_url = account.avatar_static avatar_url = account.avatar_static
avatar_data = requests.get(avatar_url).content avatar_data = requests.get(avatar_url).content
with open('feditree/avatar.png', 'wb') as handler: with open(temp_image_name, 'wb') as handler:
handler.write(avatar_data) handler.write(avatar_data)
with Image.open('feditree/avatar.png') as avatar: avatar = get_avatar()
avatar.load()
avatar = avatar.resize((100,100)).convert("RGB") avatar = avatar.resize((100,100)).convert("RGB")
avatar.paste(bola_radio, (0,0), bola_radio) avatar.paste(bola_radio, (0,0), bola_radio)
feditree.paste(avatar, coordinates[i], bola_mask) feditree.paste(avatar, coordinates[i], bola_mask)
feditree.save("feditree/feditree.png") tmp_file = (tempfile.NamedTemporaryFile(suffix='.png', delete=False)).name
feditree.save(tmp_file)
bola_radio.close() bola_radio.close()
bola_mask.close() bola_mask.close()
avatar.close() avatar.close()
@ -82,35 +110,53 @@ def create_image(accounts):
description = description + " " + _("The accounts appear in the tree in the same order as the mentions, from top to bottom and from left to right.") description = description + " " + _("The accounts appear in the tree in the same order as the mentions, from top to bottom and from left to right.")
description = description + " " + _("The order symbolizes the number of interactions, from most to least.") description = description + " " + _("The order symbolizes the number of interactions, from most to least.")
description = description + "\n\n" + _("The Fediverse logo was created by @eudaimon@fe.disroot.org and the tree design was obtained from https://freesvgdesigns.com") description = description + "\n\n" + _("The Fediverse logo was created by @eudaimon@fe.disroot.org and the tree design was obtained from https://freesvgdesigns.com")
return api.media_post("feditree/feditree.png", description=description) ret = api.media_post(tmp_file, description=description)
try:
os.remove(tmp_file)
except Exception:
pass
return ret
def get_filtered_notifications():
return [n for n in get_new_notifications(api, bot_name, ["mention"]) if n.status.visibility in ["public", "unlisted"]]
bot_name = "feditree"
localedir = './locales' localedir = './locales'
api = get_api('masto.es', bot_name) api = get_api(os.getenv("FEDI_DOMAIN"), bot_name)
notifications = get_new_notifications(api, bot_name, ["mention"]) notifications = get_filtered_notifications()
previous_ids = list_read(bot_name + "_previous_ids") i18n = gettext.translation(bot_name, localedir, fallback=True, languages=['en'])
i18n.install()
for notification in notifications: for notification in notifications:
i18n = gettext.translation(bot_name, localedir, fallback=True, languages=[notification.status.language])
i18n.install()
try: try:
if str(notification.account.id) in previous_ids: if has_user_posted(bot_name, notification.account.id):
status = "@" + notification.account.acct + " " status = "@" + notification.account.acct + " "
status += _("I have already generated a feditree for you this year. Try again next year!") status += _("I have already generated a faggotree for you this year. Try again next year!")
api.status_post(status, visibility="direct", in_reply_to_id=notification.status.id) api.status_post(status, visibility="direct", in_reply_to_id=notification.status.id)
continue continue
else: else:
list_append(bot_name + "_previous_ids", previous_ids) mark_user_posted(bot_name, notification.account.id)
accounts_ids = get_ordered_accounts_ids(notification.account.id) accounts_ids = get_ordered_accounts_ids(notification.account.id)
accounts = get_accounts(accounts_ids) accounts = get_accounts(accounts_ids)
image = create_image(accounts) image = create_image(accounts)
status = _("These are the people who have adorned the #FediTree of") + " @" + notification.account.acct + ":" status = _("These are the fags who have adorned the #FaggoTree of") + " @" + notification.account.acct + ":\n\n"
for account in accounts: for account in accounts:
if account.acct == notification.account.acct: if account.username != notification.account.username:
continue status += " - " + account.acct + "\n"
status += " @/" + account.acct
api.status_post(status, media_ids=image, visibility="unlisted", in_reply_to_id=notification.status.id) api.status_post(status, media_ids=image, visibility="unlisted", in_reply_to_id=notification.status.id)
previous_ids.append(notification.account.id) except Exception as e:
except: unmark_user_posted(bot_name, notification.account.id)
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print(exc_type, fname, exc_tb.tb_lineno)
print(e)
api.status_post(_("An error ocurred. Please try again or contact my creator"), visibility="direct", in_reply_to_id=notification.status.id) api.status_post(_("An error ocurred. Please try again or contact my creator"), visibility="direct", in_reply_to_id=notification.status.id)
if os.path.exists(temp_image_name):
try:
os.remove(temp_image_name)
except Exception:
pass
db_close()

BIN
feditree/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

View File

@ -48,9 +48,9 @@ msgstr ""
#: feditree.py:98 #: feditree.py:98
msgid "" msgid ""
"I have already generated a feditree for you this year. Try again next year!" "I have already generated a faggotree for you this year. Try again next year!"
msgstr "" msgstr ""
#: feditree.py:103 #: feditree.py:103
msgid "These are the people who have adorned the #FediTree of" msgid "These are the fags who have adorned the #FaggoTree of"
msgstr "" msgstr ""

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
python-dotenv
Mastodon.py
pillow