This commit is contained in:
bursa-pastoris 2021-12-02 23:57:42 +01:00
commit 321fa87461
15 changed files with 1315 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.mo
*.po~
.venv/
__pycache__/
log/

161
README.md Normal file
View File

@ -0,0 +1,161 @@
# Albatrobot
Albatrobot is a personal project born as a bot to dice rolling only, but has
since then evolved with the addition of more functions (most of which is
useless). But I like it even if it is substantially bloat.
## Installation and usage
### Installation
To install Albatrobot, follow those steps.
1. Download the source code
2. Move it to the installation path of your choice and run
```
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
```
3. Configure the bot as you like (see [Configuration](#configuration)
paragraph).
## Configuration
All the required configuration is in `settings.ini` file, that can be edited
either manually or through the configuration script `setup.py`. The first way
is recommended one.
The settings in `settings.ini` are described below.
* `Token`: token to connect the software to the bot user on Telegram server.
Seealso <https://core.telegram.org/bots#3-how-do-i-create-a-bot> and
<https://core.telegram.org/bots/api#authorizing-your-bot>.
* `AuthUsers`: user IDs of the users allowed to use the bot. The numbers must
be separated by commas.
* `MsgCooldown`: minimum time in seconds between two following uses of commands
`/Easter` or `/Christmas` by the same user. The time is the same, but it is
considered separately for the two commands.
* `CitePath`: path of the document containing the citations for `/Easter`
command.
* `ImgPath`: path containing the images for `/Christmas` command.
If you choose to use the configuration script, you must run it in the
installation path as
python3 setup.py
### Usage
#### Manual boot
You can boot Albatrobot with the following command:
cd <installation path> && source .venv/bin/activate && ./main.py
However, running Albatrobot like that is not recommended (except for testing
purposes). If you do that, you can then stop the bot with `Ctrl + C`.
#### Automatic boot
Albatrobot can be automatically run at system boot by adding the following
command in `crontab` queue:
@reboot sleep 10 && cd <installation path> && ./main.py > /tmp/albatrobot.txt 2>&1
To do so, run `crontab -e` and add the above line to the file that will be
opened, then save and close.
If you run Albatrobot this way - the recommended one - consider the following.
* `sleep 10` adds a delay of 10 seconds. It is necessary to allow the system
to set the network up and prevents errors due to impossibility to connect to
the Internet and to Telegram APIs. You can change the time to comply with
your necessities.
* If Albatrobot can't be launched, some error information should be saved in
`/tmp/albatrobot.txt`.
You can stop Albatrobot by killing its process. I will add a better way in the
future.
## Common problems
### When I try to start the bot Python exits with an error
Check:
1. that the configuration file content is in the correct format and contains
the correct information (see paragraph [Configuration](#configuration));
2. that you have installed the required libraries (see paragraph
[Installation](#installation));
3. that you have activate the virtual environment (see paragraph
[Installation](#installation));
4. that the virtual environment has not been changed after the installation.
In the first three cases, the solution is self-explaining. In the fourth, the
simplest way is to just delete the virtual environment and recreate it. Just
run the following commands in the installation path:
rm -rf .venv python3 -m venv .venv source .venv/bin/activate python3 -m pip install -r requirements.txt
This does not cause any data loss.
### The Albatrobot seems correctly booted but on Telegram it doesn't work or behaves oddly
Check:
1. that you have used the correct token (see paragraph
[Configuration](#configuration));
2. that only one instance of the albatrobot is active for the same Telegram bot
user
If none of the above solution has effect, try the ones of those suggested for
[Python errors](#when-i-try-to-start-the-bot-python-exits-with-an-error).
### Albatrobot says that there are no avilable citations or images
Check:
1. that there are available citations and images;
2. that citations and images are in supported formats;
3. that in the configuration the correct paths are set.
## Some notes
### About language
Albatrobot was not originally developed nor used in English and the translation
may contain mistakes. Sorry for that. If you want so, you can help improving
it.
### About the repository
This git repository does not contain all the project's development history
because of two causes.
1. At the beginning, the project was not tracked with git.
2. I chose to redistribute the software after much time of development, so the
original archive contained some data that I didn't want nor could publish.
But at that time it was way easier to create a new archive than purging
those data from the original one. And the original archive contained many
completely unuseful files.
## License
Albatrobot is proudly [free
software](https://www.gnu.org/philosophy/free-sw.html#four-freedoms) (as both
in "free speech" and "free beer") and always will be, as it is released under
[AGPL-3.0-only](https://www.gnu.org/licenses/agpl-3.0-standalone.html) license.
Its source code is publicly available at
[Disroot](https://git.disroot.org/bursapastoris/albatrobot).
## Disclaimer
Albatrobot is free software, but it works interacting with
[Telegram](https://telegram.org)'s servers and API. Most of Telegram's clients
are distributed under a free license, but *all Telegram's servers' code is
proprietary software* and as such it yields unjust power over its users and
cannot be trusted. *Albatrobot's development does not want to promote non-free
software* and is just an instructive hobby.
Please, *at least* for communication that must be kept private or secret do not
use Telegram. Use only free software and free protocols instead.
* * *
![](https://www.gnu.org/graphics/agplv3-with-text-162x68.png "AGPL-3.0 logo")

106
TRANSLATE.md Normal file
View File

@ -0,0 +1,106 @@
# Translate
This document explains how to translate the Albatrobot.
## Prerequisites
To translate the Albatrobot, you will need to have the [`gettext`
package](https://www.gnu.org/software/gettext/) from the GNU project installed
in your system. In Debian, you can install with `sudo apt install gettext`.
## Translate to a new language
To translate to a new language, create a path named
`locales/<lang>/LC_MESSAGES` (where `<lang>` is the two-letters code of the
language you are translasting to), then run `xgettext main.py albatrobot.py -o
locales/<lang>/LC_MESSAGES/template.to`.
Edit `locales/<lang>/LC_MESSAGES/template.to`. You will find several lines of
metadata and after a serie of groups of rows like the following:
#: main.py:59 msgid "help" msgstr ""
The meaning is the following.
* `main.py` is the source file the string comes from.
* `59 ` is the number of the line in the source file where the string begins.
* In `msgid "help"`, `msgid` tells that what follows is the string as it is
written in the source code, `help` is the string itself. The string is always
written between quotation marks ( `"..."`).
* In `msgstr` tells that what follow is the string translation, `aide` is the
translation itself.
To translate you must edit the content of the quotation marks after `msgstr`,
adding the translation. For example, if you were translating to French the
lines above would become
#: main.py:59 msgid "help" msgstr "aide"
Any not translates string will be printed as it is written in the source code.
For example, if you didn't translate to French the example above the Albatrobot
would have printed `help` instead of `aide`.
When you finish the translation, go to `locales/<lang>/LC_MESSAGES` and run
`msgfmt template.po`. This will produce `messages.mo`: it is a binary file with
your translation. You need it to test your translation, but you must *not*
commit it.
## Update an existing translation
### Existing strings
If you just want to update the translation of already translated strings, an
existing translation, just edit `locales/<lang>/LC_MESSAGES/template.po`, where
`<lang>` is the two-letter code of the language you are updating. For an
explanation of the content of that file, see [Translate to a new
language](#Translate to a new language).
When you are happy with your work, run `msgfmt template.to`. This will produce
`messages.mo`: it is a binary file with your translation. You need it to test
your translation, but you must *not* commit it.
### New strings
If you want to translate strings that are present in the source code but not in
`locales/template.pot`, you must update that file. To do so:
1. run `xgettext main.py albatrobot.py -o locales/template.pot`: this will
update `locales/template.pot` with the new strings;
2. run `msgmerge locales/<lang>/LC_MESSAGES/template.po locales/template.pot
--update`: this will update the translation file of the language you are
translating to.
You can now go on as desribed in [Existing strings](#Existing strings).
### String removal
If a string is removed from the main source code, the next run of `msgmerge` on
each translation will move the translation of such string to the bottom of the
`.po` file and prepend each of its lines with `#~`. You should remove them
before merging the updated translation file. However, if you forget to do so it
will be done before the next release.
## Metadata
The first lines of `template.pot` (and `template.po`) contain metadata. The
parts that must be edited are those in uppercase and their values should be
changed to the following.
metadata value
------------------------------------- -----------------------------------------
`SOME DESCRIPTIVE TITLE` `Albatrabot translation`
`YEAR THE PACKAGE'S COPYRIGHT HOLDER` `2021 bursa-pastoris`
`PACKAGE` `Albatrobot`
`FIRST AUTHOR <EMAIL@ADDRESS>, YEAR` `bursa-pastoris
<bursapastoris at disroot dot org>, 2021`
`PACKAGE VERSION` `1.2`
`YEAR-MO-DA HO:MI+ZONE` `<year>-<month>-<day>
<hour>:<minute>+<timezone>` the
translation was created at and in.
You can invent from day on, nobody
really minds about that. Please, do so
if you have privacy concerns.
`FULL NAME <EMAIL@ADDRESS>` `<translator's name or nickname>
<translator's email address>`
`LANGUAGE <LL@li.org>` `<language of the translation>
<translation team's email address>`
`CHARSET` `UTF-8`

308
albatrobot.py Normal file
View File

@ -0,0 +1,308 @@
'''
Albatrobot - Telegram bot for RPG groups and similar
Copyright (C) 2019-2021 bursa-pastoris
This file is part of Albatrobot.
Albatrobot is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License.
Albatrobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Albatrobot. If not, see <https://www.gnu.org/licenses/>.
'''
from telegram import ParseMode, ChatAction
import random
import glob
import Levenshtein
import time
# Logging
import os
import logging
from datetime import datetime
if not os.path.exists('log'):
os.mkdir('log')
formatter = logging.Formatter(
'(%(asctime)s) %(levelname)s: %(message)s')
def set_logger(name, log_file, level=logging.INFO):
'''Creates logger more easily.'''
handler = logging.FileHandler(log_file)
handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(level)
logger.addHandler(handler)
return logger
action_logger = set_logger('action_logger', 'log/albatrobot.log')
system_logger = set_logger(
'system_logger', 'log/python.log',
logging.DEBUG)
# Settings
auth_users = []
cite_path = ''
img_path = ''
# Internal functions
def closest(good_list,item):
distances={i:Levenshtein.distance(i,item) for i in good_list}
lowest_distance=distances[min(list(distances.keys()),key=distances.__getitem__)]
closest_keys=[i for i, distance in distances.items() if distance == lowest_distance]
return closest_keys
# Private use filter
from functools import wraps
def restricted(func):
'''User restriction decorator.
This decorators limits the users that are allowed to use the
functions it is applied to, i.e. all the ones for which isn't
specified anything else, to the ones whose ID is in the appropriate
list.
'''
@wraps(func)
def wrapped(update, context, *args, **kwargs):
user_id = update.effective_user.id
if user_id not in auth_users:
context.bot.send_message(
chat_id=update.effective_chat.id,
text='This bot is for private use only!')
return
return func(update, context, *args, **kwargs)
return wrapped
# Albatrobot functions
@restricted
def start(update, context):
'''Shows a message when a user contacts the bot for the firs time.'''
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("Hello, I'm Albatrobot! Type /help for help!"))
@restricted
def help(update, context):
'''Shows a help message.
This message doesn't correspond to the inline suggestion set in
@BotFather, that must be set manually through a Telegram client.
'''
instructions = (
_("*Officially supported commands*\n"
"/help: show officialy supported commands\n"
"/roll: rolls dice following DnD notation\n"
"/source: sends the bot's source code"))
context.bot.send_message(
chat_id=update.effective_chat.id,
text=instructions,
parse_mode=ParseMode.MARKDOWN_V2)
@restricted
def roll(update, context):
'''Roll a dice following DnD notation: <dice number>d<die faces>.
The function supports any dice number and any faces number, but bot
the modifiers.
'''
to_roll = str(context.args[0]) # Gets the argument
dnum = int(to_roll.split('d')[0])
dfac = int(to_roll.split('d')[1])
rolls = [] # Single rolls list
if dnum > 1000 or dfac > 1000:
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("{}, you can roll at most 1000 dice with at most 1000 faces"
" at once!").format(update.message.from_user.first_name))
else:
for n in range(dnum):
rolls.append(random.randint(1,dfac))
if len(rolls) == 1:
result = str(rolls[0])
elif len(rolls) > 1:
result = [str(roll) for roll in rolls]
result = '+'.join(result)
result = result + ' = ' + str(sum(rolls))
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("{user_name} rolled {result}").format(
user_name=update.message.from_user.first_name,
result=result
)
)
@restricted
def version(update, context):
'''Sends Albatrobot's current version.'''
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("*Albatrobot's version:* {}'").format('0\.1'),
parse_mode=ParseMode.MARKDOWN_V2
)
@restricted
def source(update, context):
'''Sends the instructions to get a copy of @Albatrobot's source code.'''
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("Albatrobot's source code is available on"
" [Disroot](https:\/\/git.disroot.org\/bursapastoris\/albatrobot)\."),
parse_mode=ParseMode.MARKDOWN_V2)
action_logger.info(
'User {} requested Albatrobot\'s source code.'.format(
update.effective_user.id))
@restricted
def easter(update, context):
'''Sends a citation from the archive
User can either choose the source or let the bot pick a random one.
'''
try:
last_msg = context.user_data['last_easter']
if datetime.now()-last_msg < msg_cooldown:
context.bot.send_message(
chat_id=update.effective_user.id,
text=_("Easter is good, but you can't invoke it so often!"
" Retry in some seconds..."))
action_logger.info('User {} wants too much Easter.'.format(
update.effective_user.id)
)
return
except KeyError:
pass
from resources.citations import citations
cits_archive = citations.citations
if len(context.args) > 0:
if context.args[0] in list(cits_archive.keys()):
author = str(context.args[0])
else:
closest_authors = closest(list(cits_archive.keys()),context.args[0])
if len(closest_authors) == 1:
suggestion = _("The most similar is {}.").format(closest_authors[0])
else:
suggestion = _("The most similar are {}.").format(
str(', '.join(i for i in closest_authors[:-1])
+ ' e '
+ closest_authors[-1])
)
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("Author {required_author} is not present in the archive!"
" {suggestion}\n"
"\n"
"N.b.: CaSe Is ImPoRtAnT!").format(
required_author=context.args[0],
suggestion=suggestion
))
action_logger.info('User {user_name} wants easter from non'
' registered author {author}.'.format(
user_name=update.effective_user.id,
author=context.args[0])
)
return
else:
author = random.choice(list(cits_archive.keys()))
context.bot.send_message(
chat_id=update.effective_chat.id,
text='{cit}\n\n~{author}'.format(
cit=random.choice(cits_archive[author]),
author=author
)
)
context.user_data['last_easter'] = datetime.now()
@restricted
def christmas(update, context):
'''Sens a (hopefully funny) image from the archive.'''
imgs = glob.glob(img_path)
try:
last_msg = context.user_data['last_christmas']
if datetime.now()-last_msg < msg_cooldown:
context.bot.send_message(
chat_id=update.effective_user.id,
text=_("Christmas is good, but you can't invoke it so often!"
" Try again in some seconds...'"))
action_logger.info(
'User {} wants too much Christmas.'.format(update.effective_user.id))
return
except KeyError:
pass
context.bot.send_chat_action(
chat_id=update.effective_chat.id,
action=ChatAction.UPLOAD_PHOTO)
context.bot.send_photo(
chat_id=update.effective_chat.id,
photo=open(random.choice(imgs), 'rb'))
context.user_data['last_christmas'] = datetime.now()
@restricted
def end_of_year_dinner(update, context):
'''Sends a user each citation from the archive.'''
from resources.citations import citations
cits_archive = citations.citations
cits_number = 0
dest = random.choice(auth_users)
context.bot.send_message(
chat_id=dest,
text=_("Hello there, here an end of year dinner offered by {} !").format(
update.message.from_user.first_name
)
)
for author in list(cits_archive.keys()):
for cit in cits_archive[author]:
context.bot.send_message(
chat_id=dest,
text='{cit}\n\n~{author}'.format(cit=cit,author=author)
)
cits_number += 1
time.sleep(1.1)
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("End of year dinner of {cits_number} plates delvered to {dest}!").format(
cits_number=cits_number,
dest=context.bot.getChatMember(chat_id=dest,user_id=dest).user.first_name
)
)
action_logger.info(
'User {} sent a end of year dinner to user {}.'.format(
update.effective_user.id,
dest)
)
@restricted
def unknown_command(update, context):
context.bot.send_message(
chat_id=update.effective_chat.id,
text=_("Unknown command."))

7
icon/attribution.md Normal file
View File

@ -0,0 +1,7 @@
The icone is a derivative work of
* Jifutari,
[Short tailed Albatross1.jpg](https://commons.wikimedia.org/wiki/File:Short_tailed_Albatross1.jpg)
(GFDL 1.2+)
* Anbox team, redraw by SweetCanadianMullet,
[Anbox logo.svg](https://commons.wikimedia.org/wiki/File:Anbox_logo.svg)
(GPL 3+)

185
icon/icon.svg Normal file
View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="60.264999mm"
height="60.264999mm"
viewBox="0 0 60.264999 60.265"
version="1.1"
id="svg8"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title856">Albatrobot icon v2.0</title>
<defs
id="defs2">
<linearGradient
id="a">
<stop
offset="0"
stop-color="#0b756b"
id="stop981" />
<stop
offset="1"
stop-color="#009184"
id="stop983" />
</linearGradient>
<linearGradient
id="b">
<stop
offset="0"
stop-color="#0d9b8e"
id="stop986" />
<stop
offset="1"
stop-color="#009184"
id="stop988" />
</linearGradient>
<linearGradient
id="d"
x1="157.77"
x2="339.84"
y1="139.82001"
y2="313.57999"
xlink:href="#a"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-36.505,-39.336)" />
<linearGradient
id="c"
x1="343.32999"
x2="640.13"
y1="39.34"
y2="334.70999"
xlink:href="#b"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-343.325,-39.336)" />
</defs>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Albatrobot icon v2.0</dc:title>
<cc:license
rdf:resource="GPL 3.0" />
<dc:rights>
<cc:Agent>
<dc:title>GPL 3.0</dc:title>
</cc:Agent>
</dc:rights>
<dc:description>Derivative of:
* Jifutari, Short tailed Albatross1.jpg (CC-BY-SA 3.0) &lt;https://commons.wikimedia.org/wiki/File:Short_tailed_Albatross1.jpg&gt;
* Anbox team, redraw by SweetCanadianMullet, Anbox logo.svg (GPL 3+) &lt;https://commons.wikimedia.org/wiki/File:Anbox_logo.svg&gt;</dc:description>
<dc:contributor>
<cc:Agent>
<dc:title>Jifutari
Anbox team
SweetCanadianMullet</dc:title>
</cc:Agent>
</dc:contributor>
<dc:date>Latest revision: 2020-11-13</dc:date>
</cc:Work>
</rdf:RDF>
</metadata>
<circle
style="display:none;fill:none;stroke:#000000;stroke-width:0.264999;stroke-linecap:round;stroke-linejoin:round"
id="path856"
cy="30.1325"
cx="30.1325"
r="30" />
<path
id="path38-6"
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 35.983325,10.829633 C 32.767365,6.7258035 25.873152,6.6397057 25.345962,6.7855472 16.042231,9.359326 15.076272,12.802738 15.031672,16.154337 c 0.177107,6.173302 1.709309,12.343139 1.709309,18.554047 v 5.918853 5.918856 c 0,0.765088 0.615876,1.381268 1.380967,1.381268 h 13.37624 c 0.765085,0 1.381268,-0.61618 1.381268,-1.381268 v -5.918856 -5.918853 c 0,-3.532554 -0.925748,-6.99884 -1.238624,-10.506257 -0.196624,-5.634172 3.739301,-2.725129 3.739301,-2.725129 l 3.039843,1.172667 1.109808,-2.943801 -5.291227,-3.073098 2.613575,0.906172 0.409769,-3.587798 z" />
<g
id="layer1"
transform="translate(11.28236,6.5511343)">
<g
id="g865">
<path
id="path848"
style="display:inline;fill:#e7babc;fill-opacity:1;stroke:#000000;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 22.956514,10.081735 c 7.491015,4.857024 3.588066,5.565126 1.141259,4.844129 2.711664,2.023006 17.782148,3.256227 18.17416,5.507818 -0.196321,0.450871 -0.384889,2.249661 1.166559,0.295451 3.75118,-4.725 -2.542339,-7.075201 -2.542339,-7.075201 -8.4768,-1.816709 -9.922161,-4.6784785 -11.924461,-5.4438695 -3.614399,-1.381629 -4.270727,-3.9315633 -4.270727,-3.9315633 0.566263,4.0221323 0.254991,6.4370498 -1.744451,5.8032358 z" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 22.956197,10.081632 c 9.75211,6.04845 11.582671,5.57684 16.382181,6.69127 2.65649,0.61683 4.301,1.04335 2.93302,3.66071"
id="path887-3" />
</g>
<g
id="layer2"
style="display:inline"
transform="translate(0,-0.06463969)">
<path
style="display:inline;fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 25.587742,9.2339705 0.873093,0.415462 c 2.671035,-0.673457 2.443572,0.5382355 3.003833,1.2376015 -1.154234,-0.01079 -2.232828,-0.220242 -3.003833,-1.2376015"
id="path846" />
<path
style="display:inline;fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 27.954628,10.690509 c 0.506573,0.138288 1.010002,0.205647 1.51004,0.196525 C 29.120392,10.328883 28.855431,9.558287 28.317154,9.5197699 27.801769,9.7601872 27.60853,10.12294 27.954628,10.690509 Z"
id="path859" />
</g>
</g>
<g
id="layer3"
transform="translate(11.28236,6.5511343)">
<g
id="g861">
<path
style="display:inline;fill:none;stroke:#000000;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 13.551728,4.8163465 c 1.142282,0.332879 1.906481,1.085558 3.42793,0.997435 1.777992,-0.546972 2.641191,-0.240162 2.778072,0.744527 -0.59649,0.965825 -2.168018,1.169988 -2.778072,-0.744527"
id="path844" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 18.26813,7.24846 C 17.553659,5.6597164 17.403994,6.6664747 18.817322,5.573689 19.16201,7.1164674 19.500477,5.93037 18.26813,7.24846 Z"
id="path863" />
</g>
</g>
<g
id="layer4"
transform="translate(11.28236,6.615774)">
<g
id="g857">
<rect
width="3.946455"
height="13.376437"
x="0.13255601"
y="26.474398"
fill="#ffffff"
ry="1.9724472"
id="rect1001"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.265112;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
width="3.946455"
height="13.376437"
x="22.977983"
y="26.474398"
fill="#ffffff"
ry="1.9724472"
id="rect1003"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.265112;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
width="3.946455"
height="13.376437"
x="14.849442"
y="34.662247"
fill="#ffffff"
ry="1.9724472"
id="rect1005"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.265112;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
width="3.946455"
height="13.376437"
x="8.1815109"
y="34.662247"
fill="#ffffff"
ry="1.9724472"
id="rect1007"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.265112;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,117 @@
# Albatrobot translation.
# Copyright (C) 2021 bursa-pastoris
# This file is distributed under the same license as the Albatrobot package.
# bursa-pastoris <bursapastoris at disroot dot org>, 2021.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-02 00:00+0000\n"
"PO-Revision-Date: 2021-12-02 00:00+0000\n"
"Last-Translator: bursa-pastoris <bursapastoris at disroot dot org>\n"
"Language-Team: Englsih <bursapastoris at disroot dot org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.py:59
msgid "help"
msgstr ""
#: main.py:62
msgid "roll"
msgstr ""
#: main.py:65
msgid "source"
msgstr ""
#: main.py:68
msgid "version"
msgstr ""
#: main.py:72
msgid "easter"
msgstr ""
#: main.py:76
msgid "christmas"
msgstr ""
#: main.py:81
msgid "end_of_year_dinner"
msgstr ""
#: albatrobot.py:96
msgid "Hello, I'm Albatrobot! Type /help for help!"
msgstr ""
#: albatrobot.py:107
msgid ""
"*Officially supported commands*\n"
"/help: show officialy supported commands\n"
"/roll: rolls dice following DnD notation\n"
"/source: sends the bot's source code"
msgstr ""
#: albatrobot.py:132
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
msgstr ""
#: albatrobot.py:146
#, python-brace-format
msgid "{user_name} rolled {result}"
msgstr ""
#: albatrobot.py:158
msgid "*Albatrobot's version:* {}'"
msgstr ""
#: albatrobot.py:168
msgid ""
"Albatrobot's source code is available on [Disroot](https:\\/\\/git.disroot."
"org\\/bursapastoris\\/albatrobot)\\."
msgstr ""
#: albatrobot.py:187
msgid ""
"Easter is good, but you can't invoke it so often! Retry in some seconds..."
msgstr ""
#: albatrobot.py:205
msgid "The most similar is {}."
msgstr ""
#: albatrobot.py:207
msgid "The most similar are {}."
msgstr ""
#: albatrobot.py:214
#, python-brace-format
msgid ""
"Author {required_author} is not present in the archive! {suggestion}\n"
"\n"
"N.b.: CaSe Is ImPoRtAnT!"
msgstr ""
#: albatrobot.py:249
msgid ""
"Christmas is good, but you can't invoke it so often! Try again in some "
"seconds...'"
msgstr ""
#: albatrobot.py:276
msgid "Hello there, here an end of year dinner offered by {} !"
msgstr ""
#: albatrobot.py:291
#, python-brace-format
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
msgstr ""
#: albatrobot.py:307
msgid "Unknown command."
msgstr ""

View File

@ -0,0 +1,132 @@
# Albatrobot translation.
# Copyright (C) 2021 bursa-pastoris
# This file is distributed under the same license as the Albatrobot package.
# bursa-pastoris <bursapastoris at disroot dot org>, 2021.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-02 00:00+0000\n"
"PO-Revision-Date: 2021-12-02 00:00+0000\n"
"Last-Translator: bursa-pastoris <bursapastoris at disroot dot org>\n"
"Language-Team: Italian <bursapastoris at disroot dot org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.py:59
msgid "help"
msgstr "aiuto"
#: main.py:62
msgid "roll"
msgstr "tira"
#: main.py:65
msgid "source"
msgstr "sorgente"
#: main.py:68
msgid "version"
msgstr "versione"
#: main.py:72
msgid "easter"
msgstr "pasqua"
#: main.py:76
msgid "christmas"
msgstr "natale"
#: main.py:81
msgid "end_of_year_dinner"
msgstr "cenone_di_fine_anno"
#: albatrobot.py:96
msgid "Hello, I'm Albatrobot! Type /help for help!"
msgstr "Ciao, sono Albatrobot! Scrivi /aiuto per aiuto!"
#: albatrobot.py:107
#, fuzzy
msgid ""
"*Officially supported commands*\n"
"/help: show officialy supported commands\n"
"/roll: rolls dice following DnD notation\n"
"/source: sends the bot's source code"
msgstr ""
"*Comandi ufficialmente supportati*\n"
"/aiuto: mostra i comandi ufficialmente supportati\n"
"/tira: tira dadi secondo la notaizone di DnD\n"
"/sorgente: manda il codice sorgente del bot"
#: albatrobot.py:132
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
msgstr ""
"{}, puoi tirare al massimo 1000 dadi con al massimo 1000 facce per volta!"
#: albatrobot.py:146
#, python-brace-format
msgid "{user_name} rolled {result}"
msgstr "{user_name} ha tirato {result}"
#: albatrobot.py:158
msgid "*Albatrobot's version:* {}'"
msgstr "*Versione dell'Albatrobot:* {}"
#: albatrobot.py:168
msgid ""
"Albatrobot's source code is available on [Disroot](https:\\/\\/git.disroot."
"org\\/bursapastoris\\/albatrobot)\\."
msgstr ""
"Il codice sorgente dell'Albatrobot è disponibile su"
" [Disroot](https:\\/\\/git.disroot.org\\/bursapastoris\\/albatrobot)\\."
#: albatrobot.py:187
msgid ""
"Easter is good, but you can't invoke it so often! Retry in some seconds..."
msgstr ""
"La Pasqua è bella, ma non puoi invocarla così spesso! Riprova tra qualche "
"secondo..."
#: albatrobot.py:205
msgid "The most similar is {}."
msgstr "Il più simile è {}."
#: albatrobot.py:207
msgid "The most similar are {}."
msgstr "I più simili sono {}."
#: albatrobot.py:214
#, python-brace-format
msgid ""
"Author {required_author} is not present in the archive! {suggestion}\n"
"\n"
"N.b.: CaSe Is ImPoRtAnT!"
msgstr ""
"L'autore {required_authr} non è presente nell'archivio! {suggestion}\n"
"\n"
"N.b.: Le MaIuScOlE sOnO iMpOrTaNtI!"
#: albatrobot.py:249
msgid ""
"Christmas is good, but you can't invoke it so often! Try again in some "
"seconds...'"
msgstr ""
"Il Natale è bello, ma non puoi invocarlo così spesso! RIprova tra qualche "
"secondo..."
#: albatrobot.py:276
msgid "Hello there, here an end of year dinner offered by {} !"
msgstr ""
#: albatrobot.py:291
#, python-brace-format
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
msgstr ""
#: albatrobot.py:307
msgid "Unknown command."
msgstr ""

117
locales/template.pot Normal file
View File

@ -0,0 +1,117 @@
# Albatrobot translation.
# Copyright (C) 2021 bursa-pastoris
# This file is distributed under the same license as the Albatrobot package.
# bursa-pastoris <bursapastoris at disroot dot org>, 2021.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-02 00:00+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.py:59
msgid "help"
msgstr ""
#: main.py:62
msgid "roll"
msgstr ""
#: main.py:65
msgid "source"
msgstr ""
#: main.py:68
msgid "version"
msgstr ""
#: main.py:72
msgid "easter"
msgstr ""
#: main.py:76
msgid "christmas"
msgstr ""
#: main.py:81
msgid "end_of_year_dinner"
msgstr ""
#: albatrobot.py:96
msgid "Hello, I'm Albatrobot! Type /help for help!"
msgstr ""
#: albatrobot.py:107
msgid ""
"*Officially supported commands*\n"
"/help: show officialy supported commands\n"
"/roll: rolls dice following DnD notation\n"
"/source: sends the bot's source code"
msgstr ""
#: albatrobot.py:132
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
msgstr ""
#: albatrobot.py:146
#, python-brace-format
msgid "{user_name} rolled {result}"
msgstr ""
#: albatrobot.py:158
msgid "*Albatrobot's version:* {}'"
msgstr ""
#: albatrobot.py:168
msgid ""
"Albatrobot's source code is available on [Disroot](https:\\/\\/git.disroot."
"org\\/bursapastoris\\/albatrobot)\\."
msgstr ""
#: albatrobot.py:187
msgid ""
"Easter is good, but you can't invoke it so often! Retry in some seconds..."
msgstr ""
#: albatrobot.py:205
msgid "The most similar is {}."
msgstr ""
#: albatrobot.py:207
msgid "The most similar are {}."
msgstr ""
#: albatrobot.py:214
#, python-brace-format
msgid ""
"Author {required_author} is not present in the archive! {suggestion}\n"
"\n"
"N.b.: CaSe Is ImPoRtAnT!"
msgstr ""
#: albatrobot.py:249
msgid ""
"Christmas is good, but you can't invoke it so often! Try again in some "
"seconds...'"
msgstr ""
#: albatrobot.py:276
msgid "Hello there, here an end of year dinner offered by {} !"
msgstr ""
#: albatrobot.py:291
#, python-brace-format
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
msgstr ""
#: albatrobot.py:307
msgid "Unknown command."
msgstr ""

92
main.py Executable file
View File

@ -0,0 +1,92 @@
#!/bin/python3
'''
Albatrobot - Telegram bot for RPG groups and similar
Copyright (C) 2019-2021 bursa-pastoris
This file is part of Albatrobot.
Albatrobot is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License.
Albatrobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Albatrobot. If not, see <https://www.gnu.org/licenses/>.
'''
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import albatrobot
from datetime import timedelta
# Settings
import configparser
config = configparser.ConfigParser()
config.read('settings.ini')
token = config['Bot']['Token']
for var in config['Settings']['AuthUsers'].split(','):
albatrobot.auth_users.append(int(var))
albatrobot.msg_cooldown = timedelta(
seconds=int(config['Settings']['MsgCooldown']))
albatrobot.cite_path = config['Resources']['CitePath']+'/*'
albatrobot.img_path = config['Resources']['ImgPath']+'/*'
lang = config['Settings']['Language']
# Localization
import gettext
transl = gettext.translation('messages', localedir='locales',languages=[lang])
transl.install()
_ = transl.gettext
# Link to bot platform
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
# Bot building - Basic functions
start_handler = CommandHandler('start', albatrobot.start)
dispatcher.add_handler(start_handler)
help_handler = CommandHandler(["help",_("help")], albatrobot.help)
dispatcher.add_handler(help_handler)
roll_handler = CommandHandler(["roll",_("roll")], albatrobot.roll)
dispatcher.add_handler(roll_handler)
source_handler = CommandHandler(["source",_("source")], albatrobot.source)
dispatcher.add_handler(source_handler)
version_handler = CommandHandler(["version",_("version")], albatrobot.version)
dispatcher.add_handler(version_handler)
# Bot building - Secret functions
easter_handler = CommandHandler(["easter",_("easter")], albatrobot.easter)
dispatcher.add_handler(easter_handler)
christmas_handler = CommandHandler(
["christmas",_("christmas")],
albatrobot.christmas)
dispatcher.add_handler(christmas_handler)
end_of_year_dinner_handler = CommandHandler(
["end_of_year_dinner",_("end_of_year_dinner")],
albatrobot.end_of_year_dinner)
dispatcher.add_handler(end_of_year_dinner_handler)
unknown_handler = MessageHandler(
Filters.command,
albatrobot.unknown_command)
dispatcher.add_handler(unknown_handler)
# Start bot
updater.start_polling()

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
python-telegram-bot==13.0
python-Levenshtein==0.12.2

View File

@ -0,0 +1 @@
citations = {'Albatrobot':['no citations available']}

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="227.25415mm"
height="136.32494mm"
viewBox="0 0 227.25415 136.32494"
version="1.1"
id="svg964"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="no-imgs.svg"
inkscape:export-filename="/home/kzt//.tmp/no-available-images.png"
inkscape:export-xdpi="599.961"
inkscape:export-ydpi="599.961"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview966"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.61557941"
inkscape:cx="448.35808"
inkscape:cy="325.7094"
inkscape:window-width="1588"
inkscape:window-height="856"
inkscape:window-x="3"
inkscape:window-y="35"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-right="20"
fit-margin-bottom="20" />
<defs
id="defs961" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-159.2345,-88.183751)">
<text
xml:space="preserve"
style="font-size:35.2778px;line-height:1;font-family:FreeM;-inkscape-font-specification:FreeM;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="273.00266"
y="124.05876"
id="text2457"><tspan
sodipodi:role="line"
id="tspan2455"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:35.2778px;font-family:FreeMono;-inkscape-font-specification:FreeMono;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="273.00266"
y="124.05876">no</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:35.2778px;font-family:FreeMono;-inkscape-font-specification:FreeMono;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="273.00266"
y="160.75595"
id="tspan2461">available</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:35.2778px;font-family:FreeMono;-inkscape-font-specification:FreeMono;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="273.00266"
y="197.45312"
id="tspan5257">images</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

11
settings.ini Normal file
View File

@ -0,0 +1,11 @@
[Bot]
Token:
[Settings]
AuthUsers:
MsgCooldown: 5
[Resources]
CitePath: resources/citations
ImgPath: resources/imgs