Merge branch 'dev'
This commit is contained in:
commit
d5c0a608de
|
@ -1,9 +0,0 @@
|
|||
## v0.2
|
||||
|
||||
* Added admins allowed to stop the bot from Telegram
|
||||
* Completely anonymized logs
|
||||
* Added privacy info
|
||||
* Improved logs
|
||||
* License compliance and update
|
||||
* General cleanup
|
||||
* Documentation fixes
|
|
@ -1,20 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
I develop Albatrobot as a bot for personal purposes and therefore it is not
|
||||
supposed to receive external contributes. However, if you have a proposal I'd
|
||||
be glad to look at it. I will consider contributes received through pull
|
||||
requests on the [Disroot
|
||||
repository](https://git.disroot.org/bursa-pastoris/albatrobot/pulls) or by
|
||||
email at `bursapastoris at disroot dot org`, as either patches or pull-requests
|
||||
from other repositories. If you want to send contributes by email, see
|
||||
[here](https://git.disroot.org/bursapastoris/info/contact.md) for more
|
||||
information about how to contact me by email and
|
||||
[here](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)
|
||||
for how to use `git format-patch`.
|
||||
|
||||
Please note that I may take some time to answer.
|
||||
|
||||
Also, consider that I will merge only contributes that are useful to me, as I
|
||||
use and develop Albatrobot for my own needs only. However, you can take
|
||||
advantage of the rights granted by [AGPL-3.0-only](./LICENSE) license and
|
||||
fork the project as you prefer.
|
131
README.md
131
README.md
|
@ -1,8 +1,13 @@
|
|||
# Albatrobot
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
Albatrobot is a personal project born as a simple bot to roll dice, but has
|
||||
since then evolved with the addition of more functions, most of which is
|
||||
useless. It thus evolved to the current bloat hodgepodge - but I like it the
|
||||
same.
|
||||
|
||||
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.
|
||||
|
||||
## Setup
|
||||
|
||||
|
@ -17,28 +22,46 @@ To install Albatrobot, follow those steps.
|
|||
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 must edited
|
||||
manually.
|
||||
All the required configuration is in `settings.ini` file, that must edited
|
||||
manually. Some keys already contain a value: those are suggested values, that
|
||||
can be tuned to user's needs.
|
||||
|
||||
| Option | Meaning |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Token` | Token to connect the bot script to the bot user on Telegram server.[^token] |
|
||||
| `Users` | Comma-separated user IDs of the users allowed to use the bot. |
|
||||
| `Admins` | Comma-separated user IDs of the users with admin powers. Admins are always considered Users as well. |
|
||||
| `MsgCooldown` | Minimum time in seconds between two following uses of commands `/Easter` and `/Christmas` by the same user. The time is the same but is considered separately for each command. |
|
||||
| `CitePath` | Path of the document containing the citations for `/Easter` command. |
|
||||
| `ImgPath` | Path of the document containing the images for `Christmas` command. |
|
||||
- `Bot`
|
||||
- `Token`: the token to connect the bot script to the bot user on Telegram
|
||||
server.[^token]
|
||||
- `Users`
|
||||
- `Users`: a comma-separated list of the user IDs of the users allowed to use
|
||||
the bot
|
||||
- `Admins`: a comma-separated user IDs of the users with admin powers. Admins
|
||||
are always considered Users as well.
|
||||
- `Settings`
|
||||
- `Language`: the language of the bot interface
|
||||
- `MsgCooldown`: the minimum time, in seconds, between two following uses of
|
||||
commands `/Easter` and `/Christmas` by the same user. The time is the same
|
||||
but is considered separately for each command.
|
||||
- `Proxy`: proxy settings to connect to Telegram's bot API. The string must
|
||||
follow the syntax
|
||||
`<protocol>://[<username[:<password>]@]<hostname>[:<port>]` (e.g.
|
||||
`socks5://127.0.0.1:9050`)
|
||||
- `YellLang`: language for `/yell` voice messages
|
||||
- `YellSpeed`: speed for `/yell` voice messages
|
||||
- `Resources`
|
||||
- `CitePath`: path of the document containing the citations for `/Easter`
|
||||
command.
|
||||
- `ImgPath`: path of the document containing the images for `Christmas`
|
||||
command.
|
||||
|
||||
### Usage
|
||||
|
||||
Albatrobot can be automatically run at system boot by adding the following command in `crontab`'s queue the following command:
|
||||
Albatrobot can be automatically run at system boot by adding the following
|
||||
command in `crontab`'s queue the following command:
|
||||
|
||||
@reboot sleep 10 && cd <installation path> && ./main.py > /tmp/albatrobot.txt 2>&1
|
||||
```
|
||||
@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.
|
||||
|
@ -54,30 +77,58 @@ If you run Albatrobot this way - the recommended one - consider the following:
|
|||
|
||||
If the bot is stopped, it can be started again with the following commands:
|
||||
|
||||
cd <installation path>
|
||||
source .venv/bin/activate
|
||||
nohup ./main.py &
|
||||
```
|
||||
cd <installation path>
|
||||
source .venv/bin/activate
|
||||
nohup ./main.py &
|
||||
```
|
||||
|
||||
The bot can be stopped by killing its process on the machine it runs on or by
|
||||
any admin with the `/stop` command.
|
||||
|
||||
For testing purposes, Albatrobot can be run with the following command:
|
||||
|
||||
cd <installation path> && source .venv/bin/activate && ./main.py
|
||||
```
|
||||
cd <installation path> && source .venv/bin/activate && ./main.py
|
||||
```
|
||||
|
||||
It can then be killed with `Ctrl + C`.
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
Commands available for all users:
|
||||
|
||||
- `/christmas`: get an image
|
||||
- `/easter (<author>)`: get a citation from given author, or a random one if no
|
||||
author is specified.
|
||||
- `/end_of_year_dinner`: send all citations in the archive to a random user.
|
||||
- `/epiphony (<text>)`: get a voice message from Albatrobot reading given text
|
||||
- `/help`: get help about bot commands.
|
||||
- `/legal`: get legal info about privacy and copyright.
|
||||
- `/roll <dice>(<modifiers>)`: roll dice, with or without modifiers. Launch
|
||||
without arguments to get help on the modifiers.
|
||||
- `/version`: get Albatrobot's version
|
||||
|
||||
Additional commands for bot admins:
|
||||
|
||||
- `/init`: reinitialize the bot. This updates inline suggestions and purges
|
||||
/epiphony cached files.
|
||||
- `/stop`: stop Albatrobot. To be used only in case of emergency: restarting
|
||||
the bot after this command is used requires direct access to the server.
|
||||
|
||||
|
||||
## 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
|
||||
3. that you are using the virtual environment (see paragraph
|
||||
[Installation](#installation));
|
||||
4. that the virtual environment has not been changed after the installation.
|
||||
|
||||
|
@ -85,27 +136,28 @@ 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
|
||||
```
|
||||
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
|
||||
### The bot seems correctly booted but 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
|
||||
**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.
|
||||
|
@ -119,23 +171,16 @@ repository](https://git.disroot.org/bursa-pastoris/albatrobot) or send an email
|
|||
to `bursapastoris at disroot dot org` taking the precautions described
|
||||
[here](https://git.disroot.org/bursa-pastoris/info/src/branch/master/contact.md).
|
||||
|
||||
## 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
|
||||
## A note 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
|
||||
those data from the original one. Also, the original archive contained many
|
||||
completely unuseful files.
|
||||
|
||||
## Legal
|
||||
|
@ -151,4 +196,4 @@ For all legal info see [LEGAL.md](./LEGAL.md).
|
|||
bot?](https://core.telegram.org/bots#3-how-do-i-create-a-bot) paragraph on
|
||||
*Bots: An introduction for developers* and [Authorizing your
|
||||
bot](https://core.telegram.org/bots/api#authorizing-your-bot) paragraph of
|
||||
*Telegram Bot API* on [Telegram's API portal](https://core.telegram.org/)
|
||||
*Telegram Bot API* on [Telegram's API portal](https://core.telegram.org/)
|
||||
|
|
520
albatrobot.py
520
albatrobot.py
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
Albatrobot - Telegram bot for RPG groups and similar
|
||||
Copyright (C) 2019, 2020, 2021, 2021 bursa-pastoris
|
||||
Copyright (C) 2019, 2020, 2021, 2022 bursa-pastoris
|
||||
|
||||
This file is part of Albatrobot.
|
||||
|
||||
|
@ -17,79 +17,88 @@
|
|||
along with Albatrobot. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import random
|
||||
import glob
|
||||
import time
|
||||
import os
|
||||
import psutil
|
||||
import signal
|
||||
|
||||
import csv
|
||||
from datetime import datetime
|
||||
import glob
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import pyttsx3
|
||||
import random
|
||||
from re import findall as find
|
||||
import signal
|
||||
import time
|
||||
|
||||
import Levenshtein
|
||||
from telegram import ParseMode, ChatAction
|
||||
import psutil
|
||||
from rolldice import roll_dice, rolldice
|
||||
from telegram import BotCommand
|
||||
from telegram.constants import ChatAction, ChatType, ParseMode
|
||||
|
||||
from constants import *
|
||||
|
||||
# Prepare paths
|
||||
required_paths = ['log',TMPVOICEDIR]
|
||||
for i in required_paths:
|
||||
if not os.path.exists(i):
|
||||
os.mkdir(i)
|
||||
|
||||
# Logging
|
||||
|
||||
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):
|
||||
"""Create 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
|
||||
users = []
|
||||
admins = []
|
||||
cite_path = ""
|
||||
img_path = ""
|
||||
logger = logging.getLogger('albatrobot_logger')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logformat = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
|
||||
loghandler_toconsole = logging.StreamHandler()
|
||||
loghandler_toconsole.setLevel(logging.DEBUG)
|
||||
loghandler_toconsole.setFormatter(logformat)
|
||||
loghandler_tofile = logging.FileHandler('log/albatrobot.log')
|
||||
loghandler_tofile.setLevel(logging.DEBUG)
|
||||
loghandler_tofile.setFormatter(logformat)
|
||||
logger.addHandler(loghandler_toconsole)
|
||||
logger.addHandler(loghandler_tofile)
|
||||
|
||||
|
||||
# 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
|
||||
"""Return the item(s) of good_list closest to item.
|
||||
|
||||
"Closest" means "with the shortest Levenshtein distance.
|
||||
"""
|
||||
distances = {}
|
||||
for i in good_list:
|
||||
i_dist = Levenshtein.distance(i,item)
|
||||
if i_dist not in distances:
|
||||
distances[i_dist] = list()
|
||||
distances[i_dist].append(i)
|
||||
closest_items = distances[min(distances.keys())]
|
||||
return closest_items
|
||||
|
||||
|
||||
def unauthorized_user(update, context):
|
||||
context.bot.send_message(
|
||||
async def unauthorized_user(update, context):
|
||||
"""Inform the user that he is not authorized to used the bot."""
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_("User not authorized.")
|
||||
text=_('User not authorized.')
|
||||
)
|
||||
|
||||
|
||||
def unknown_command(update, context):
|
||||
context.bot.send_message(
|
||||
async def unknown_command(update, context):
|
||||
"""Inform the user that the command does not exist."""
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_("Unknown command.")
|
||||
text=_('Unknown command.')
|
||||
)
|
||||
|
||||
|
||||
def error_handler(update, context):
|
||||
system_logger.error(context.error)
|
||||
print(context.error)
|
||||
#def error_handler(update, context): # This should be deeply reworked. Sooner or later.
|
||||
# system_logger.error(context.error)
|
||||
# print(context.error)
|
||||
|
||||
|
||||
# Albatrobot base functions
|
||||
def start(update, context):
|
||||
# Basic functions
|
||||
async def start(update, context):
|
||||
"""Show a message when a user contacts the bot for the first time."""
|
||||
context.bot.send_message(
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_("Hello, I'm Albatrobot\! Type \/help for help\!\n"
|
||||
"\n"
|
||||
|
@ -103,212 +112,363 @@ def start(update, context):
|
|||
)
|
||||
|
||||
|
||||
def stop(update, context):
|
||||
async def stop(update, context):
|
||||
"""Stop the bot."""
|
||||
pid = os.getpid()
|
||||
parent = psutil.Process(pid)
|
||||
action_logger.info("Albatrobot was terminated.")
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('Albatrobot will be stopped in a few moments.')
|
||||
)
|
||||
logger.info('Albatrobot was stopped.')
|
||||
parent.send_signal(signal.SIGTERM)
|
||||
|
||||
|
||||
def help(update, context):
|
||||
"""
|
||||
Show a help message.
|
||||
|
||||
async def help(update, context):
|
||||
"""Show a help message.
|
||||
|
||||
The 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"
|
||||
"/legal: send legal info about privacy and copyright\n"
|
||||
"/roll: roll dice following DnD notation\n"
|
||||
"\n"
|
||||
"Admins can also use /stop to stop the bot from Telegram in case of"
|
||||
" emergency\.")
|
||||
# Common commands
|
||||
ccom_header = _('*Albatrobot commands*')
|
||||
ccom_help = {
|
||||
_('christmas'):_('get an image'),
|
||||
_('easter')+_(' `\(\<author\>\)`'):_('get a citation from given author,'
|
||||
' or a random one if no author is specified\.'),
|
||||
_('end\_of\_year\_dinner'):_('send all citations in the archive to a'
|
||||
' random user\.'),
|
||||
_('epiphony')+_(' `\(\<text\>\)`'):_('get a voice message from'
|
||||
' Albatrobot reading given text'),
|
||||
_('help'):_('get help about bot commands\.'),
|
||||
_('legal'):_('get legal info about privacy and copyright\.'),
|
||||
_('roll')+_(' `\<dice\>\(\<modifiers\>\)`'):_('roll dice, with or'
|
||||
' without modifiers\. Launch without arguments to get help on the'
|
||||
' modifiers\.'),
|
||||
_('version'):_("get Albatrobot's version")
|
||||
}
|
||||
ccom_help = [f'\- \/{i}: {ccom_help[i]}'
|
||||
for i in ccom_help]
|
||||
ccom_help.sort()
|
||||
ccom_help = '\n'.join(ccom_help)
|
||||
ccom_help = f'{ccom_header}\n{ccom_help}'
|
||||
|
||||
# Admin commands
|
||||
acom_header = _('*Additional commands for bot admins*')
|
||||
acom_help = {
|
||||
_('init'):_('reinitialize the bot\. This updates inline suggestions'
|
||||
' and purges /epiphony cached files\.'),
|
||||
_('stop'):_('stop Albatrobot\. To be used _only_ in case of emergency:'
|
||||
' restarting the bot after this command is used requires direct'
|
||||
' access to the server\.')
|
||||
}
|
||||
acom_help = [f'\- \/{i}: {acom_help[i]}'
|
||||
for i in acom_help]
|
||||
acom_help.sort()
|
||||
acom_help = '\n'.join(acom_help)
|
||||
acom_help = f'{acom_header}\n{acom_help}'
|
||||
|
||||
# Addendum
|
||||
non_admin_addendum = _(
|
||||
'Bot admins have access to additional commands and can get help on'
|
||||
' them using /help in a private chat\.'
|
||||
)
|
||||
context.bot.send_message(
|
||||
|
||||
# Actually sending stuff
|
||||
if (update.effective_chat.type == ChatType.PRIVATE
|
||||
and update.effective_user.id in ADMINS):
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_user.id,
|
||||
text=_(f'{ccom_help}\n\n{acom_help}'),
|
||||
parse_mode=ParseMode.MARKDOWN_V2
|
||||
)
|
||||
else:
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_(f'{ccom_help}\n\n{non_admin_addendum}'),
|
||||
parse_mode=ParseMode.MARKDOWN_V2
|
||||
)
|
||||
|
||||
|
||||
async def init(update, context):
|
||||
"""Initialize the bot."""
|
||||
# Set commands suggestions
|
||||
commands=[BotCommand(_('roll'),_('Roll dice'))]
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=instructions,
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
text=_('Initializing...'))
|
||||
await context.bot.set_my_commands(commands)
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('Done'))
|
||||
# Delete /epiphony files
|
||||
for i in os.listdir(TMPVOICEDIR):
|
||||
os.remove(f'{TMPVOICEDIR}/{i}')
|
||||
|
||||
|
||||
def legal(update, context):
|
||||
async def legal(update, context):
|
||||
"""
|
||||
Send legal information (privacy and copyright).
|
||||
"""
|
||||
context.bot.send_chat_action(
|
||||
await context.bot.send_chat_action(
|
||||
chat_id=update.effective_chat.id,
|
||||
action=ChatAction.UPLOAD_DOCUMENT)
|
||||
with open("LEGAL.md","rb") as legalnotes:
|
||||
context.bot.send_document(
|
||||
with open('doc/LEGAL.md','rb') as legalnotes:
|
||||
await context.bot.send_document(
|
||||
chat_id=update.effective_chat.id,
|
||||
document=legalnotes,
|
||||
filename="albatrobot-legal-notes.txt")
|
||||
action_logger.info("A user asked for legal info.")
|
||||
filename='albatrobot-legal-notes.txt')
|
||||
logger.info('A user asked for legal info.')
|
||||
|
||||
|
||||
def version(update, context):
|
||||
async def version(update, context):
|
||||
"""Send Albatrobot's current version."""
|
||||
context.bot.send_message(
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_("*Albatrobot's version:* {}'").format('0\.2'),
|
||||
text=_("*Albatrobot's version:* {}").format(VERSION.replace('.','\.')),
|
||||
parse_mode=ParseMode.MARKDOWN_V2
|
||||
)
|
||||
|
||||
|
||||
# Albatrobot commands
|
||||
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
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Albatrobot easter eggs
|
||||
def christmas(update, context):
|
||||
# Commands
|
||||
async def christmas(update, context):
|
||||
"""Send a (hopefully funny) image from the archive."""
|
||||
imgs = glob.glob(img_path)
|
||||
imgs = glob.glob(str(IMGPATH+'/*'))
|
||||
try:
|
||||
last_msg = context.user_data["last_christmas"]
|
||||
if datetime.now()-last_msg < msg_cooldown:
|
||||
context.bot.send_message(
|
||||
last_msg = context.user_data['last_christmas']
|
||||
if datetime.now()-last_msg < MSGCOOLDOWN:
|
||||
await 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...'")
|
||||
text=_("Christmas is good, but you can't invoke it so often! "
|
||||
"Try again in some seconds...")
|
||||
)
|
||||
action_logger.info("A user wants too much Christmas.")
|
||||
logger.info('A user wants too much Christmas.')
|
||||
return
|
||||
# If last_christmas doesn't exist, it just means the user never used
|
||||
# /christmas since last bot start.
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
context.bot.send_chat_action(
|
||||
|
||||
await context.bot.send_chat_action(
|
||||
chat_id=update.effective_chat.id,
|
||||
action=ChatAction.UPLOAD_PHOTO
|
||||
)
|
||||
context.bot.send_photo(
|
||||
await context.bot.send_photo(
|
||||
chat_id=update.effective_chat.id,
|
||||
photo=open(random.choice(imgs), "rb")
|
||||
photo=open(random.choice(imgs), 'rb')
|
||||
)
|
||||
context.user_data["last_christmas"] = datetime.now()
|
||||
context.user_data['last_christmas'] = datetime.now()
|
||||
|
||||
|
||||
def easter(update, context):
|
||||
"""
|
||||
Send a citation from the archive.
|
||||
|
||||
async def easter(update, context):
|
||||
"""Send 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(
|
||||
if datetime.now()-last_msg < MSGCOOLDOWN:
|
||||
await 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("A user wants too much Easter.")
|
||||
logger.info('A user wants too much Easter.')
|
||||
return
|
||||
# If last_easter doesn't exist, it just means the user never used /easter
|
||||
# since last bot start.
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
from resources.citations import citations
|
||||
cits_archive = citations.citations
|
||||
with open(CITARCHIVE,'r') as cits_file:
|
||||
cits = csv.DictReader(cits_file)
|
||||
authors = tuple({row['author'] for row in cits})
|
||||
|
||||
# If the user asked for an author...
|
||||
if len(context.args) > 0:
|
||||
if context.args[0] in list(cits_archive.keys()):
|
||||
# ...and it exists
|
||||
if context.args[0] in authors:
|
||||
author = str(context.args[0])
|
||||
# ...and it does not
|
||||
else:
|
||||
closest_authors = closest(list(cits_archive.keys()),context.args[0])
|
||||
closest_authors = closest(authors,context.args[0])
|
||||
if len(closest_authors) == 1:
|
||||
suggestion = _("The most similar is {}.").format(closest_authors[0])
|
||||
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(
|
||||
last_author = closest_authors[-1]
|
||||
other_authors = ', '.join(closest_authors[:-1])
|
||||
suggestion = _('The most similar are {} and {}.').format(
|
||||
other_authors, last_author)
|
||||
await 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(
|
||||
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("A user wants easter from non registered"
|
||||
" author {author}.".format(author=context.args[0]))
|
||||
logger.info('A user wants easter from non registered'
|
||||
' author {author}.'.format(author=context.args[0]))
|
||||
return
|
||||
# If he did not
|
||||
else:
|
||||
author = random.choice(list(cits_archive.keys()))
|
||||
|
||||
context.bot.send_message(
|
||||
author = random.choice(authors)
|
||||
|
||||
with open(CITARCHIVE,'r') as cits_file:
|
||||
cits = csv.DictReader(cits_file)
|
||||
cits_pool = [cit['text'] for cit in cits if cit['author'] == author]
|
||||
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text="{cit}\n\n~{author}".format(
|
||||
cit=random.choice(cits_archive[author]),
|
||||
text=_('{cit}\n\n~{author}').format(
|
||||
cit=random.choice(cits_pool),
|
||||
author=author
|
||||
)
|
||||
)
|
||||
context.user_data["last_easter"] = datetime.now()
|
||||
context.user_data['last_easter'] = datetime.now()
|
||||
|
||||
|
||||
def end_of_year_dinner(update, context):
|
||||
"""Sends a user each citation from the archive."""
|
||||
async def end_of_year_dinner(update, context):
|
||||
"""Send a user each citation from the archive."""
|
||||
# Importing here is so that the citations list can be updated without
|
||||
# restarting the bot.
|
||||
from resources.citations import citations
|
||||
|
||||
cits_archive = citations.citations
|
||||
with open(CITARCHIVE,'r') as cits_file:
|
||||
cits_archive = list(csv.DictReader(cits_file))
|
||||
cits_number = 0
|
||||
dest = random.choice(users)
|
||||
|
||||
context.bot.send_message(
|
||||
chat_id=dest,
|
||||
text=_("Hello there, here an end of year dinner offered by {} !").format(
|
||||
dest_user_id = random.choice(USERS)
|
||||
dest_user = await context.bot.getChatMember(chat_id=dest_user_id,user_id=dest_user_id)
|
||||
dest_user_firstname = dest_user.user.first_name
|
||||
|
||||
await context.bot.send_message(
|
||||
chat_id=dest_user_id,
|
||||
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) # To avoid hitting Telegram's message rate limit
|
||||
|
||||
context.bot.send_message(
|
||||
for cit in cits_archive:
|
||||
await context.bot.send_message(
|
||||
chat_id=dest_user_id,
|
||||
text=_('{cit}\n\n~{author}'.format(cit=cit['text'],author=cit['author']))
|
||||
)
|
||||
cits_number +=1
|
||||
time.sleep(1.1) # To avoid hitting Telegram's message rate limit
|
||||
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_("End of year dinner of {cits_number} plates delvered to {dest}!").format(
|
||||
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
|
||||
dest=dest_user_firstname
|
||||
)
|
||||
)
|
||||
action_logger.info("A user sent an end of year dinner.")
|
||||
logger.info('A user sent an end of year dinner.')
|
||||
|
||||
|
||||
async def epiphony(update, context):
|
||||
sentence = ' '.join(context.args).upper()
|
||||
if len(sentence) == 0:
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('What should I say?')
|
||||
)
|
||||
else:
|
||||
sentence_hash = hashlib.md5(sentence.encode(encoding='UTF-8')).hexdigest()
|
||||
voice_file = f'{TMPVOICEDIR}/{sentence_hash}.ogg'
|
||||
await context.bot.send_chat_action(
|
||||
chat_id=update.effective_chat.id,
|
||||
action=ChatAction.RECORD_VOICE)
|
||||
if not os.path.exists(voice_file):
|
||||
engine = pyttsx3.init()
|
||||
engine.setProperty('voice',YELLLANG)
|
||||
engine.setProperty('rate',YELLSPEED)
|
||||
engine.save_to_file(sentence,voice_file)
|
||||
engine.runAndWait()
|
||||
time.sleep(1) # Workaround to prevent ReferenceError due to using
|
||||
# pyttsx3 with threads
|
||||
await context.bot.send_voice(
|
||||
chat_id=update.effective_chat.id,
|
||||
voice=open(voice_file,'rb'))
|
||||
|
||||
|
||||
async def roll(update, context):
|
||||
"""Roll a dice following DnD notation: <dice number>d<die faces>.
|
||||
|
||||
Many modifiers are supported. Dice number and faces are limited to 500
|
||||
each, to prevent DoS effects.
|
||||
"""
|
||||
to_roll = ''.join(context.args)
|
||||
if len(to_roll) == 0:
|
||||
roll_modifiers = {
|
||||
'\+n':_('add `n` to the roll'),
|
||||
'\-n':_('subtract `n` from the roll'),
|
||||
'\*n':_('multiply the roll by `n`'),
|
||||
'/n':_('divide the roll by `n`'),
|
||||
'//n':_('floor divide the roll by `n`'),
|
||||
'\*\*n':_('exponentiate the roll to `n`'),
|
||||
'K\(n\)':_('keep highest `n` rolls \(defaults to `1`\)'),
|
||||
'k\(n\)':_('keep lowest `n` rolls \(defaults to `1`\)'),
|
||||
'X\(n\)':_('drop highest `n` rolls \(defaults to `1`\)'),
|
||||
'x\(n\)':_('drop lowest `n` rolls \(defaults to `1`\)'),
|
||||
'\!\(n\)':_('explode rolls equal to `n` \(defaults to die size\)'),
|
||||
'\!\<n':_('explode rolls lower than `n`'),
|
||||
'\!\>n':_('explode rolls higher than `n`'),
|
||||
'f\<n':_('count failures as rolls lower than `n`'),
|
||||
'f\>n':_('count failures as rolls higher than `n`'),
|
||||
'\<n':_('count successes as rolls lower than `n`'),
|
||||
'\>n':_('count successes as rolls higher than `n`'),
|
||||
'\!p':_('penetrate rolls equal to dice size'),
|
||||
'\!p\<n':_('penetrate rolls lower than `n`'),
|
||||
'\!p\>n':_('penetrate rolls higher than `n`'),
|
||||
'an':_('add `n` to each roll'),
|
||||
'sn':_('subtract `n` from each roll'),
|
||||
'mn':_('multiply by `n` each roll'),
|
||||
'R\(n\)':_('reroll each dice that rolled `n` until there are no'
|
||||
' `n` left\(defaults to `1`\)'),
|
||||
'r\(n\)':_('reroll once each dice that rolled `n` \(defaults to'
|
||||
' 1\)'),
|
||||
'R\<n':_('reroll dice that rolled lower than `n` until there are'
|
||||
' no results lower than `n` left'),
|
||||
'R\>n':_('reroll dice that rolled higher than `n` until there'
|
||||
' no results higher than `n` left'),
|
||||
'r\<n':_('reroll once dice that roll lower than `n`'),
|
||||
'r\>n':_('reroll once dice that roll higher than `n`')
|
||||
}
|
||||
roll_modifiers = [f'\- `{i}`: {roll_modifiers[i]}' for i in roll_modifiers]
|
||||
roll_modifiers = '\n'.join(roll_modifiers)
|
||||
roll_help = _(
|
||||
"To roll, use the syntax `\<number of dice\>d\<dice size\>`\. You"
|
||||
" can also use the following modifiers\. Note that:\n"
|
||||
"\- `\(n\)` means that an integer number may be specified, but if"
|
||||
" it isn't the specified default value is assumed\n"
|
||||
"\- `n` means that an integer number is required\n"
|
||||
"\- any other symbol means exactly that symbol\n"
|
||||
"\n"
|
||||
"\n"
|
||||
)+roll_modifiers # "+" is a workaround to incompatibility between
|
||||
# f-strings and parse_mode
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=roll_help,
|
||||
parse_mode=ParseMode.MARKDOWN_V2,
|
||||
disable_web_page_preview=True
|
||||
)
|
||||
elif max([int(i) for i in find('\d+',to_roll)]) > 500: # Prevent DoS
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('You can roll up to 500 dice with up to 500 faces at once!')
|
||||
)
|
||||
else:
|
||||
try:
|
||||
the_roll = roll_dice(to_roll)
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('{user_name} rolled {rolls} = {result}').format(
|
||||
user_name=update.message.from_user.first_name,
|
||||
rolls=the_roll[1],result=the_roll[0]
|
||||
)
|
||||
)
|
||||
except rolldice.DiceGroupException:
|
||||
await context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=_('You used a wrong syntax!')
|
||||
)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import configparser
|
||||
from datetime import timedelta
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read("settings.ini")
|
||||
|
||||
# Bot
|
||||
TOKEN = config["Bot"]["Token"]
|
||||
|
||||
# Users
|
||||
ADMINS = [int(admin) for admin in config["Users"]["Admins"].split(",")]
|
||||
USERS = [int(user) for user in config["Users"]["Users"].split(",")]
|
||||
|
||||
# Settings
|
||||
LANG = config["Settings"]["Language"]
|
||||
MSGCOOLDOWN = timedelta(seconds=int(config["Settings"]["MsgCooldown"]))
|
||||
PROXY = config["Settings"]["Proxy"]
|
||||
YELLLANG = config['Settings']['YellLang']
|
||||
YELLSPEED = config['Settings']['YellSpeed']
|
||||
|
||||
# Resources
|
||||
CITARCHIVE = config["Resources"]["CitArchive"]
|
||||
IMGPATH = config["Resources"]["ImgPath"]
|
||||
|
||||
# Private
|
||||
TMPVOICEDIR = 'tmp'
|
||||
VERSION = "0.3"
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
## v0.3
|
||||
### New features
|
||||
|
||||
- Inline suggestions
|
||||
- New commands:
|
||||
- `/epiphony`, to let Albatrobot speak
|
||||
- `/init`, to set again commands suggestions and clean `/epiphony` "cache"
|
||||
- Support for proxy
|
||||
- Support for roll modifiers
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Better help texts
|
||||
- Enhance "no images available" image
|
||||
|
||||
### Code
|
||||
|
||||
- Constants moved to own module
|
||||
|
||||
### Misc
|
||||
|
||||
- Update dependencies, including `python-telegram-bot` to v20
|
||||
- Misc cleanup and fixes
|
||||
|
||||
|
||||
## v0.2
|
||||
|
||||
* Added admins allowed to stop the bot from Telegram
|
||||
* Completely anonymized logs
|
||||
* Added privacy info
|
||||
* Improved logs
|
||||
* License compliance and update
|
||||
* General cleanup
|
||||
* Documentation fixes
|
|
@ -0,0 +1,21 @@
|
|||
I develop ALbatrobot for my own use.
|
||||
|
||||
Everyone is welcome to propose his contribution:
|
||||
|
||||
1. through pull requests on the [Disroot
|
||||
repository](https://git.disroot.org/bursa-pastoris/albatrobot/pulls), or
|
||||
2. by email at `bursapastoris at disroot dot org`, as either patches or pull
|
||||
requests from other repositories.[^email]
|
||||
|
||||
But note that, as Albatrobot is a personal project, I will merge only the
|
||||
contributions that I find useful or interesting *for me*. However, everyone
|
||||
can enjoy the rights granted by [AGPL-3.0-only license](./LICENSE) to fork the
|
||||
project and do whatever they want on their copies (as long as the license is
|
||||
honoured).
|
||||
|
||||
|
||||
[^email]: If you want to send contributes by email, see
|
||||
[here](https://git.disroot.org/bursapastoris/info/contact.md) for more
|
||||
information about how to contact me by email and
|
||||
[here](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)
|
||||
for how to use `git format-patch`.
|
|
@ -1,6 +1,7 @@
|
|||
## Privacy
|
||||
|
||||
Albatrobot's developer does not process any personal data under the definition of the
|
||||
Albatrobot's developer does not process any personal data under the definition
|
||||
of the
|
||||
[GDPR](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0679)
|
||||
and therefore is not a data controller.
|
||||
|
||||
|
@ -25,14 +26,14 @@ Also, anonymous logs are stored:
|
|||
* `/easter`, if the user hits the time limit or asks for an unknown author
|
||||
* whenever an error is raised
|
||||
|
||||
The logs are intended for debug and issue resolution only and never contain
|
||||
The logs are intended for debug and issue resolution only and *never* contain
|
||||
personal data.
|
||||
|
||||
## Copyright
|
||||
|
||||
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
|
||||
in "free speech" and "free beer"), and always will be, as it is released under
|
||||
[AGPL-3.0-only](./LICENSE.md) license. Its source code is publicly available
|
||||
at [Disroot](https://git.disroot.org/bursapastoris/albatrobot).
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
To release a new version:
|
||||
|
||||
1. Checkout a new branch named `release-v{release}`
|
||||
2. Complete the checklist *in given order*. If anything changes, restart from
|
||||
the first step.
|
||||
|
||||
- [ ] Check if dependencies are up to date
|
||||
- [ ] Reorder stuff in source code (imports, requirements, function
|
||||
definitions, command handlers...)
|
||||
- [ ] Update docstrings and comments
|
||||
- [ ] Update /help messages
|
||||
- [ ] Update translation
|
||||
- [ ] the template
|
||||
- [ ] each known language
|
||||
- [ ] Update version number
|
||||
- [ ] in `constants.py`
|
||||
- [ ] in the updated translations
|
||||
- [ ] Update documentation not listed below
|
||||
- [ ] Update `doc/README.md`
|
||||
- [ ] Update `doc/CHANGELOG.md`
|
||||
- [ ] _At least now_, test _each command_, both as admin and simple user, to
|
||||
identify new bugs; testing English is mandatory, all other languages are
|
||||
recommended
|
||||
- [ ] If the items were checked, *uncheck them*!
|
||||
|
||||
Using at least one commit per item is recommended. They can be squashed
|
||||
afterwards.
|
||||
|
||||
3. Merge the branch in `master`
|
||||
4. Tag the merge commit as `v{release}` and push
|
|
@ -1,43 +1,51 @@
|
|||
# Translate
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
This document explains how to translate the Albatrobot.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To translate the Albatrobot, you will need to have the [`gettext`
|
||||
To translate the Albatrobot, you 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`, 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:
|
||||
metadata and after a serie of groups of rows like the following:
|
||||
|
||||
#: main.py:59
|
||||
msgid "help"
|
||||
msgstr ""
|
||||
```
|
||||
#: 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.
|
||||
* In `msgstr ""` tells that what follow is the string translation. It will be
|
||||
empty because the string was never translated.
|
||||
|
||||
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"
|
||||
```
|
||||
#: 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
|
||||
|
@ -52,11 +60,10 @@ commit it.
|
|||
|
||||
### 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).
|
||||
If you just want to update the translation of already translated strings, 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
|
||||
|
@ -75,13 +82,25 @@ If you want to translate strings that are present in the source code but not in
|
|||
|
||||
You can now go on as desribed in [Existing strings](#Existing strings).
|
||||
|
||||
Running `msgmerge` may mark some translations with `#, fuzzy`, such as in
|
||||
|
||||
```
|
||||
#: main.py:59
|
||||
#, fuzzy
|
||||
msgid "help"
|
||||
msgstr "aide"
|
||||
```
|
||||
|
||||
This means that something in that these translation changed. You can check them
|
||||
and if necessary fix them and then remove the `#, fuzzy` line.
|
||||
|
||||
### 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.
|
||||
before committing the updated translation file. However, if you forget to do so
|
||||
it will be done before the next release.
|
||||
|
||||
## Metadata
|
||||
|
||||
|
@ -91,17 +110,17 @@ changed to the following.
|
|||
|
||||
| metadata | value |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `SOME DESCRIPTIVE TITLE` | `Albatrabot translation` |
|
||||
| `SOME DESCRIPTIVE TITLE` | `Albatrabot translation.` |
|
||||
| | |
|
||||
| `YEAR THE PACKAGE'S COPYRIGHT HOLDER` | `2021 bursa-pastoris` |
|
||||
| `YEAR THE PACKAGE'S COPYRIGHT HOLDER` | `2021, 2023 bursa-pastoris` |
|
||||
| | |
|
||||
| `PACKAGE` | `Albatrobot` |
|
||||
| | |
|
||||
| `FIRST AUTHOR <EMAIL@ADDRESS>, YEAR` | `bursa-pastoris <bursapastoris at disroot dot org>, 2021` |
|
||||
| `FIRST AUTHOR <EMAIL@ADDRESS>, YEAR` | `bursa-pastoris <bursapastoris at disroot dot org>, 2021, 2023.` |
|
||||
| | |
|
||||
| `PACKAGE VERSION` | `1.2` |
|
||||
| `PACKAGE VERSION` | `0.3` |
|
||||
| | |
|
||||
| `YEAR-MO-DA HO:MI+ZONE` | `<year>-<month>-<day> <hour>:<minute>+<timezone>` the translation was created at and in.[^creation-time] |
|
||||
| `YEAR-MO-DA HO:MI+ZONE` | `<year>-<month>-<day> <hour>:<minute>+<timezone>` the translation was created at and in.[^creation-time] |
|
||||
| | |
|
||||
| `FULL NAME <EMAIL@ADDRESS>` | `<translator's name or nickname> <translator's email address>` |
|
||||
| | |
|
|
@ -1,63 +1,71 @@
|
|||
# Albatrobot translation.
|
||||
# Copyright (C) 2021 bursa-pastoris
|
||||
# Translation template for Albatrobot.
|
||||
# Copyright (C) 2021, 2023 bursa-pastoris
|
||||
# This file is distributed under the same license as the Albatrobot package.
|
||||
# bursa-pastoris <bursapastoris at disroot dot org>, 2021.
|
||||
# bursa-pastoris <bursapastoris at disroot dot org>, 2021, 2023.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.2\n"
|
||||
"Project-Id-Version: Albatrobot 0.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-01 19:49+0100\n"
|
||||
"PO-Revision-Date: 2022-01-01 19:53+0100\n"
|
||||
"Last-Translator: bursa-pastoris <bursapastoris at disroot dot org>\n"
|
||||
"Language-Team: Englsih <bursapastoris at disroot dot org>\n"
|
||||
"POT-Creation-Date: 2023-03-23 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:68 main.py:72 main.py:76
|
||||
#: main.py:48 main.py:60 albatrobot.py:161
|
||||
msgid "stop"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:79
|
||||
#: main.py:48 main.py:69 albatrobot.py:159
|
||||
msgid "init"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:65 albatrobot.py:143
|
||||
msgid "help"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:82
|
||||
#: main.py:73 albatrobot.py:144
|
||||
msgid "legal"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:85
|
||||
#: main.py:77 albatrobot.py:148
|
||||
msgid "version"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:90
|
||||
msgid "roll"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:95
|
||||
#: main.py:84 albatrobot.py:136
|
||||
msgid "christmas"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:98
|
||||
#: main.py:88 albatrobot.py:137
|
||||
msgid "easter"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:101
|
||||
#: main.py:92
|
||||
msgid "end_of_year_dinner"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:73
|
||||
#: main.py:96 albatrobot.py:141
|
||||
msgid "epiphony"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:100 albatrobot.py:145 albatrobot.py:196
|
||||
msgid "roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:81
|
||||
msgid "User not authorized."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:80
|
||||
#: albatrobot.py:89
|
||||
msgid "Unknown command."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:94
|
||||
#: albatrobot.py:103
|
||||
msgid ""
|
||||
"Hello, I'm Albatrobot\\! Type \\/help for help\\!\n"
|
||||
"\n"
|
||||
|
@ -69,49 +77,139 @@ msgid ""
|
|||
msgstr ""
|
||||
|
||||
#: albatrobot.py:121
|
||||
msgid "Albatrobot will be stopped in a few moments."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:134
|
||||
msgid "*Albatrobot commands*"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:136
|
||||
msgid "get an image"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid " `\\(\\<author\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid ""
|
||||
"*Officially supported commands*\n"
|
||||
"/help: show officialy supported commands\n"
|
||||
"/legal: send legal info about privacy and copyright\n"
|
||||
"/roll: roll dice following DnD notation\n"
|
||||
"\n"
|
||||
"Admins can also use /stop to stop the bot from Telegram in case of emergency"
|
||||
"\\."
|
||||
"get a citation from given author, or a random one if no author is "
|
||||
"specified\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:154
|
||||
msgid "*Albatrobot's version:* {}'"
|
||||
#: albatrobot.py:139
|
||||
msgid "end\\_of\\_year\\_dinner"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:174
|
||||
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
|
||||
#: albatrobot.py:139
|
||||
msgid "send all citations in the archive to a random user\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:187
|
||||
#: albatrobot.py:141
|
||||
msgid " `\\(\\<text\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:141
|
||||
msgid "get a voice message from Albatrobot reading given text"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:143
|
||||
msgid "get help about bot commands\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:144
|
||||
msgid "get legal info about privacy and copyright\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid " `\\<dice\\>\\(\\<modifiers\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid ""
|
||||
"roll dice, with or without modifiers\\. Launch without arguments to get help "
|
||||
"on the modifiers\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:148
|
||||
msgid "get Albatrobot's version"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:157
|
||||
msgid "*Additional commands for bot admins*"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:159
|
||||
msgid ""
|
||||
"reinitialize the bot\\. This updates inline suggestions and purges /epiphony "
|
||||
"cached files\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:161
|
||||
msgid ""
|
||||
"stop Albatrobot\\. To be used _only_ in case of emergency: restarting the "
|
||||
"bot after this command is used requires direct access to the server\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:173
|
||||
msgid ""
|
||||
"Bot admins have access to additional commands and can get help on them "
|
||||
"using /help in a private chat\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:182
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {result}"
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{acom_help}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:188
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{non_admin_addendum}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:196
|
||||
msgid "Roll dice"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:199
|
||||
msgid "Initializing..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:203
|
||||
msgid ""
|
||||
"Christmas is good, but you can't invoke it so often! Try again in some "
|
||||
"seconds...'"
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:233
|
||||
#: albatrobot.py:228
|
||||
msgid "*Albatrobot's version:* {}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:242
|
||||
msgid ""
|
||||
"Christmas is good, but you can't invoke it so often! Try again in some "
|
||||
"seconds..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:273
|
||||
msgid ""
|
||||
"Easter is good, but you can't invoke it so often! Retry in some seconds..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:250
|
||||
#: albatrobot.py:296
|
||||
msgid "The most similar is {}."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:252
|
||||
msgid "The most similar are {}."
|
||||
#: albatrobot.py:300
|
||||
msgid "The most similar are {} and {}."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:260
|
||||
#: albatrobot.py:304
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Author {required_author} is not present in the archive! {suggestion}\n"
|
||||
|
@ -119,11 +217,170 @@ msgid ""
|
|||
"N.b.: CaSe Is ImPoRtAnT!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:295
|
||||
msgid "Hello there, here an end of year dinner offered by {} !"
|
||||
#: albatrobot.py:324 albatrobot.py:352
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{cit}\n"
|
||||
"\n"
|
||||
"~{author}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:309
|
||||
#: albatrobot.py:345
|
||||
msgid "Hello there, here an end of year dinner offered by {}!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:359
|
||||
#, python-brace-format
|
||||
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:372
|
||||
msgid "What should I say?"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:402
|
||||
msgid "add `n` to the roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:403
|
||||
msgid "subtract `n` from the roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:404
|
||||
msgid "multiply the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:405
|
||||
msgid "divide the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:406
|
||||
msgid "floor divide the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:407
|
||||
msgid "exponentiate the roll to `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:408
|
||||
msgid "keep highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:409
|
||||
msgid "keep lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:410
|
||||
msgid "drop highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:411
|
||||
msgid "drop lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:412
|
||||
msgid "explode rolls equal to `n` \\(defaults to die size\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:413
|
||||
msgid "explode rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:414
|
||||
msgid "explode rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:415
|
||||
msgid "count failures as rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:416
|
||||
msgid "count failures as rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:417
|
||||
msgid "count successes as rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:418
|
||||
msgid "count successes as rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:419
|
||||
msgid "penetrate rolls equal to dice size"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:420
|
||||
msgid "penetrate rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:421
|
||||
msgid "penetrate rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:422
|
||||
msgid "add `n` to each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:423
|
||||
msgid "subtract `n` from each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:424
|
||||
msgid "multiply by `n` each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:425
|
||||
msgid ""
|
||||
"reroll each dice that rolled `n` until there are no `n` left\\(defaults to "
|
||||
"`1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:427
|
||||
msgid "reroll once each dice that rolled `n` \\(defaults to 1\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:429
|
||||
msgid ""
|
||||
"reroll dice that rolled lower than `n` until there are no results lower than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:431
|
||||
msgid ""
|
||||
"reroll dice that rolled higher than `n` until there no results higher than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:433
|
||||
msgid "reroll once dice that roll lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:434
|
||||
msgid "reroll once dice that roll higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:439
|
||||
msgid ""
|
||||
"To roll, use the syntax `\\<number of dice\\>d\\<dice size\\>`\\. You can "
|
||||
"also use the following modifiers\\. Note that:\n"
|
||||
"\\- `\\(n\\)` means that an integer number may be specified, but if it isn't "
|
||||
"the specified default value is assumed\n"
|
||||
"\\- `n` means that an integer number is required\n"
|
||||
"\\- any other symbol means exactly that symbol\n"
|
||||
"\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:458
|
||||
msgid "You can roll up to 500 dice with up to 500 faces at once!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:465
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {rolls} = {result}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:473
|
||||
msgid "You used a wrong syntax!"
|
||||
msgstr ""
|
||||
|
|
|
@ -1,63 +1,70 @@
|
|||
# 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.
|
||||
# bursa-pastoris <bursapastoris at disroot dot org>, 2021, 2023.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.2\n"
|
||||
"Project-Id-Version: 0.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-01 19:49+0100\n"
|
||||
"PO-Revision-Date: 2022-01-01 19:55+0100\n"
|
||||
"POT-Creation-Date: 2023-03-23 00:00+0000\n"
|
||||
"PO-Revision-Date: 2023-03-0 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"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: main.py:68 main.py:72 main.py:76
|
||||
#: main.py:48 main.py:60 albatrobot.py:161
|
||||
msgid "stop"
|
||||
msgstr "stop"
|
||||
|
||||
#: main.py:79
|
||||
#: main.py:48 main.py:69 albatrobot.py:159
|
||||
msgid "init"
|
||||
msgstr "imposta"
|
||||
|
||||
#: main.py:65 albatrobot.py:143
|
||||
msgid "help"
|
||||
msgstr "aiuto"
|
||||
|
||||
#: main.py:82
|
||||
#: main.py:73 albatrobot.py:144
|
||||
msgid "legal"
|
||||
msgstr "legge"
|
||||
msgstr "burocrazia"
|
||||
|
||||
#: main.py:85
|
||||
#: main.py:77 albatrobot.py:148
|
||||
msgid "version"
|
||||
msgstr "versione"
|
||||
|
||||
#: main.py:90
|
||||
msgid "roll"
|
||||
msgstr "tira"
|
||||
|
||||
#: main.py:95
|
||||
#: main.py:84 albatrobot.py:136
|
||||
msgid "christmas"
|
||||
msgstr "natale"
|
||||
|
||||
#: main.py:98
|
||||
#: main.py:88 albatrobot.py:137
|
||||
msgid "easter"
|
||||
msgstr "pasqua"
|
||||
|
||||
#: main.py:101
|
||||
#: main.py:92
|
||||
msgid "end_of_year_dinner"
|
||||
msgstr "cenone_di_fine_anno"
|
||||
|
||||
#: albatrobot.py:73
|
||||
#: main.py:96 albatrobot.py:141
|
||||
msgid "epiphony"
|
||||
msgstr "epifonia"
|
||||
|
||||
#: main.py:100 albatrobot.py:145 albatrobot.py:196
|
||||
msgid "roll"
|
||||
msgstr "tira"
|
||||
|
||||
#: albatrobot.py:81
|
||||
msgid "User not authorized."
|
||||
msgstr "Utente non autorizzato."
|
||||
|
||||
#: albatrobot.py:80
|
||||
#: albatrobot.py:89
|
||||
msgid "Unknown command."
|
||||
msgstr "Comando sconosciuto."
|
||||
|
||||
#: albatrobot.py:94
|
||||
#: albatrobot.py:103
|
||||
msgid ""
|
||||
"Hello, I'm Albatrobot\\! Type \\/help for help\\!\n"
|
||||
"\n"
|
||||
|
@ -76,77 +83,353 @@ msgstr ""
|
|||
"bursapastoris\\/albatrobot)\\._"
|
||||
|
||||
#: albatrobot.py:121
|
||||
#, fuzzy
|
||||
msgid "Albatrobot will be stopped in a few moments."
|
||||
msgstr "L'Albatrobot sarà arrestato tra un istante."
|
||||
|
||||
#: albatrobot.py:134
|
||||
msgid "*Albatrobot commands*"
|
||||
msgstr "*Comandi dell'Albatrobot*"
|
||||
|
||||
#: albatrobot.py:136
|
||||
msgid "get an image"
|
||||
msgstr "ricevi un'immagine"
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid " `\\(\\<author\\>\\)`"
|
||||
msgstr " `\\(\\<autore\\>\\)`"
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid ""
|
||||
"*Officially supported commands*\n"
|
||||
"/help: show officialy supported commands\n"
|
||||
"/legal: send legal info about privacy and copyright\n"
|
||||
"/roll: roll dice following DnD notation\n"
|
||||
"\n"
|
||||
"Admins can also use /stop to stop the bot from Telegram in case of emergency"
|
||||
"\\."
|
||||
"get a citation from given author, or a random one if no author is "
|
||||
"specified\\."
|
||||
msgstr ""
|
||||
"*Comandi ufficialmente supportati*\n"
|
||||
"/aiuto: mostra i comandi ufficialmente supportati\n"
|
||||
"/legge: invia le informazioni legali su privacy e diritto d'autore\n"
|
||||
"/tira: tira un dado secondo la notazione di DnD"
|
||||
"\n"
|
||||
"Gli amministratori possono anche usare /stop per arrestre il bot da Telegram "
|
||||
"in caso di emergenza\\."
|
||||
"ricevi una citazione dall'autore indicato, o da uno casuale se non viene "
|
||||
"specificato nessun autore\\."
|
||||
|
||||
#: albatrobot.py:154
|
||||
msgid "*Albatrobot's version:* {}'"
|
||||
msgstr "*Versione dell'Albatrobot:* {}"
|
||||
#: albatrobot.py:139
|
||||
msgid "end\\_of\\_year\\_dinner"
|
||||
msgstr "cenone\\_di\\_fine\\_anno"
|
||||
|
||||
#: albatrobot.py:174
|
||||
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
|
||||
#: albatrobot.py:139
|
||||
msgid "send all citations in the archive to a random user\\."
|
||||
msgstr "invia tutte le citazioni nell'archivio a un utente casuale\\."
|
||||
|
||||
#: albatrobot.py:141
|
||||
msgid " `\\(\\<text\\>\\)`"
|
||||
msgstr " `\\(\\testo\\>\\)`"
|
||||
|
||||
#: albatrobot.py:141
|
||||
msgid "get a voice message from Albatrobot reading given text"
|
||||
msgstr ""
|
||||
"{}, puoi tirare al massimo 1000 dadi con al massimo 1000 facce per volta!"
|
||||
"ricevi un messaggio vocale dall'Albatrobot che legge il testo specificato"
|
||||
|
||||
#: albatrobot.py:187
|
||||
#: albatrobot.py:143
|
||||
msgid "get help about bot commands\\."
|
||||
msgstr "ricevi aiuto sui comandi del bot\\."
|
||||
|
||||
#: albatrobot.py:144
|
||||
msgid "get legal info about privacy and copyright\\."
|
||||
msgstr "ricevi informazioni legali su riservatezza e diritto d'autore\\."
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid " `\\<dice\\>\\(\\<modifiers\\>\\)`"
|
||||
msgstr " `\\dadi\\>\\(\\<modificatori\\>\\)`"
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid ""
|
||||
"roll dice, with or without modifiers\\. Launch without arguments to get help "
|
||||
"on the modifiers\\."
|
||||
msgstr ""
|
||||
"tira dei dadi, con o senza modificatori\\. Lancia senza argomenti per "
|
||||
"ricevere aiuto sui modificatori\\."
|
||||
|
||||
#: albatrobot.py:148
|
||||
msgid "get Albatrobot's version"
|
||||
msgstr "ricevi la versione dell'Albatrobot"
|
||||
|
||||
#: albatrobot.py:157
|
||||
msgid "*Additional commands for bot admins*"
|
||||
msgstr "*Comandi aggiuntivi per gli amministratori del bot*"
|
||||
|
||||
#: albatrobot.py:159
|
||||
msgid ""
|
||||
"reinitialize the bot\\. This updates inline suggestions and purges /epiphony "
|
||||
"cached files\\."
|
||||
msgstr ""
|
||||
"reimposta il bot\\. Questo aggiorna i suggerimenti in linea e rimuove i "
|
||||
"documenti conservati per /epifonia\\."
|
||||
|
||||
#: albatrobot.py:161
|
||||
msgid ""
|
||||
"stop Albatrobot\\. To be used _only_ in case of emergency: restarting the "
|
||||
"bot after this command is used requires direct access to the server\\."
|
||||
msgstr ""
|
||||
"arresta l'ALbatrobot\\. Da usare _solo_ in caso di emergenza: riavviare il "
|
||||
"bot dopo che questo comando è stato utilizzato richiede accesso diretto al "
|
||||
"server\\."
|
||||
|
||||
#: albatrobot.py:173
|
||||
msgid ""
|
||||
"Bot admins have access to additional commands and can get help on them "
|
||||
"using /help in a private chat\\."
|
||||
msgstr ""
|
||||
"Gli amministratori del bot hanno accesso a comandi aggiuntivi e possono "
|
||||
"ricevere aiuto su di essi usando /aiuto in una conversazione privata\\."
|
||||
|
||||
#: albatrobot.py:182
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {result}"
|
||||
msgstr "{user_name} ha tirato {result}"
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{acom_help}"
|
||||
msgstr ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{acom_help}"
|
||||
|
||||
#: albatrobot.py:188
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{non_admin_addendum}"
|
||||
msgstr ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{non_admin_addendum}"
|
||||
|
||||
#: albatrobot.py:196
|
||||
msgid "Roll dice"
|
||||
msgstr "Tira dei dadi"
|
||||
|
||||
#: albatrobot.py:199
|
||||
msgid "Initializing..."
|
||||
msgstr "Reimpostazione in corso..."
|
||||
|
||||
#: albatrobot.py:203
|
||||
msgid "Done"
|
||||
msgstr "Fatto"
|
||||
|
||||
#: albatrobot.py:228
|
||||
msgid "*Albatrobot's version:* {}"
|
||||
msgstr "*Versione dell'Albatrobot:* {}"
|
||||
|
||||
#: albatrobot.py:242
|
||||
msgid ""
|
||||
"Christmas is good, but you can't invoke it so often! Try again in some "
|
||||
"seconds...'"
|
||||
"seconds..."
|
||||
msgstr ""
|
||||
"Il Natale è bello, ma non puoi invocarlo così spesso! RIprova tra qualche "
|
||||
"secondo..."
|
||||
|
||||
#: albatrobot.py:233
|
||||
#: albatrobot.py:273
|
||||
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:250
|
||||
#: albatrobot.py:296
|
||||
msgid "The most similar is {}."
|
||||
msgstr "Il più simile è {}."
|
||||
|
||||
#: albatrobot.py:252
|
||||
msgid "The most similar are {}."
|
||||
msgstr "I più simili sono {}."
|
||||
#: albatrobot.py:300
|
||||
msgid "The most similar are {} and {}."
|
||||
msgstr "I più simili sono {} e {}."
|
||||
|
||||
#: albatrobot.py:260
|
||||
#: albatrobot.py:304
|
||||
#, 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"
|
||||
"L'autore {required_author} non è presente nell'archivio! {suggestion}\n"
|
||||
"\n"
|
||||
"N.b.: Le MaIuScOlE sOnO iMpOrTaNtI!"
|
||||
|
||||
#: albatrobot.py:295
|
||||
msgid "Hello there, here an end of year dinner offered by {} !"
|
||||
#: albatrobot.py:324 albatrobot.py:352
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{cit}\n"
|
||||
"\n"
|
||||
"~{author}"
|
||||
msgstr ""
|
||||
"{cit}\n"
|
||||
"\n"
|
||||
"~{author}"
|
||||
|
||||
#: albatrobot.py:345
|
||||
msgid "Hello there, here an end of year dinner offered by {}!"
|
||||
msgstr "Ciao, ecco un cenone di fine anno offerta da {}!"
|
||||
|
||||
#: albatrobot.py:309
|
||||
#: albatrobot.py:359
|
||||
#, python-brace-format
|
||||
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
|
||||
msgstr "Cenone di fine anno di {cits_number} portate consegnato a {dest}!"
|
||||
|
||||
#: albatrobot.py:372
|
||||
msgid "What should I say?"
|
||||
msgstr "Cosa dovrei dire?"
|
||||
|
||||
#: albatrobot.py:402
|
||||
msgid "add `n` to the roll"
|
||||
msgstr "aggiungi `n` al tiro"
|
||||
|
||||
#: albatrobot.py:403
|
||||
msgid "subtract `n` from the roll"
|
||||
msgstr "sottrati `n` dal tiro"
|
||||
|
||||
#: albatrobot.py:404
|
||||
msgid "multiply the roll by `n`"
|
||||
msgstr "moltiplica il tiro per `n`"
|
||||
|
||||
#: albatrobot.py:405
|
||||
msgid "divide the roll by `n`"
|
||||
msgstr "dividi il tiro per `n`"
|
||||
|
||||
#: albatrobot.py:406
|
||||
msgid "floor divide the roll by `n`"
|
||||
msgstr "ottieni il quoto del tiro diviso per `n`"
|
||||
|
||||
#: albatrobot.py:407
|
||||
msgid "exponentiate the roll to `n`"
|
||||
msgstr "eleva il tiro alla potenza `n`"
|
||||
|
||||
#: albatrobot.py:408
|
||||
msgid "keep highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr "tieni gli `n` tiri più alti \\(valore predefinito: `1`\\)"
|
||||
|
||||
#: albatrobot.py:409
|
||||
msgid "keep lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr "tieni gli `n` tiri più bassi \\(valore predefinito: `1`\\)"
|
||||
|
||||
#: albatrobot.py:410
|
||||
msgid "drop highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr "scarta gli `n` tiri più alti \\(valore predefinito: `1`\\)"
|
||||
|
||||
#: albatrobot.py:411
|
||||
msgid "drop lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr "scarta gli `n` tiri più bassi \\(valore predefinito: `1`\\)"
|
||||
|
||||
#: albatrobot.py:412
|
||||
msgid "explode rolls equal to `n` \\(defaults to die size\\)"
|
||||
msgstr ""
|
||||
"fai esplodere i tiri pari a `n` \\(valore predefinito: dimensione del dado\\)"
|
||||
|
||||
#: albatrobot.py:413
|
||||
msgid "explode rolls lower than `n`"
|
||||
msgstr "fai esplodere i tiri minori di `n`"
|
||||
|
||||
#: albatrobot.py:414
|
||||
msgid "explode rolls higher than `n`"
|
||||
msgstr "fai esplodere i tiri maggiori di `n`"
|
||||
|
||||
#: albatrobot.py:415
|
||||
msgid "count failures as rolls lower than `n`"
|
||||
msgstr "conta i fallimenti come i tiri minori di `n`"
|
||||
|
||||
#: albatrobot.py:416
|
||||
msgid "count failures as rolls higher than `n`"
|
||||
msgstr "conta i fallimenti come i tiri maggiori di `n`"
|
||||
|
||||
#: albatrobot.py:417
|
||||
msgid "count successes as rolls lower than `n`"
|
||||
msgstr "conta i successi come i tiri minori di `n`"
|
||||
|
||||
#: albatrobot.py:418
|
||||
msgid "count successes as rolls higher than `n`"
|
||||
msgstr "conta i successi come i tiri maggiori di `n`"
|
||||
|
||||
#: albatrobot.py:419
|
||||
msgid "penetrate rolls equal to dice size"
|
||||
msgstr "penetra i tiri pari alla dimensione del dado"
|
||||
|
||||
#: albatrobot.py:420
|
||||
msgid "penetrate rolls lower than `n`"
|
||||
msgstr "penetra i tiri minori di `n`"
|
||||
|
||||
#: albatrobot.py:421
|
||||
msgid "penetrate rolls higher than `n`"
|
||||
msgstr "penetra i tiri maggiori di `n`"
|
||||
|
||||
#: albatrobot.py:422
|
||||
msgid "add `n` to each roll"
|
||||
msgstr "aggiungi `n` a ogni tiro"
|
||||
|
||||
#: albatrobot.py:423
|
||||
msgid "subtract `n` from each roll"
|
||||
msgstr "sottrai `n` a ogni tiro"
|
||||
|
||||
#: albatrobot.py:424
|
||||
msgid "multiply by `n` each roll"
|
||||
msgstr "moltiplica ogni tiro per `n`"
|
||||
|
||||
#: albatrobot.py:425
|
||||
msgid ""
|
||||
"reroll each dice that rolled `n` until there are no `n` left\\(defaults to "
|
||||
"`1`\\)"
|
||||
msgstr ""
|
||||
"ritira ogni dado con risultato `n` finché non ci sono `n` rimanenti "
|
||||
"\\(valore predefinito: `1`\\)"
|
||||
|
||||
#: albatrobot.py:427
|
||||
msgid "reroll once each dice that rolled `n` \\(defaults to 1\\)"
|
||||
msgstr ""
|
||||
"ritira una volta ogni dado che con risultato `n` \\(valore predefinito: 1\\)"
|
||||
|
||||
#: albatrobot.py:429
|
||||
msgid ""
|
||||
"reroll dice that rolled lower than `n` until there are no results lower than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
"ritira i dadi con risultato minore di `n` finché non ci sono risultati "
|
||||
"minori di `n` rimanenti"
|
||||
|
||||
#: albatrobot.py:431
|
||||
msgid ""
|
||||
"reroll dice that rolled higher than `n` until there no results higher than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
"ritira i dadi con risultato maggiore di `n` finché non ci sono risultati "
|
||||
"maggiori di `n` rimanenti"
|
||||
|
||||
#: albatrobot.py:433
|
||||
msgid "reroll once dice that roll lower than `n`"
|
||||
msgstr "ritira una volta i dadi con risultati minore di `n`"
|
||||
|
||||
#: albatrobot.py:434
|
||||
msgid "reroll once dice that roll higher than `n`"
|
||||
msgstr "ritira una volta i dadi con risultato maggiore di `n`"
|
||||
|
||||
#: albatrobot.py:439
|
||||
msgid ""
|
||||
"To roll, use the syntax `\\<number of dice\\>d\\<dice size\\>`\\. You can "
|
||||
"also use the following modifiers\\. Note that:\n"
|
||||
"\\- `\\(n\\)` means that an integer number may be specified, but if it isn't "
|
||||
"the specified default value is assumed\n"
|
||||
"\\- `n` means that an integer number is required\n"
|
||||
"\\- any other symbol means exactly that symbol\n"
|
||||
"\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Per tirare, usa la sintassi `\\<numero di dadi\\>d\\<dimensione dei "
|
||||
"dadi\\>`\\. Puoi anche usare i modificatori seguenti\\. Tieni presente che:\n"
|
||||
"\\- `\\(n\\)` significa che può essere specificato un numero intero, ma se "
|
||||
"non è specificato viene usato il valore predefinito\n"
|
||||
"\\- qualunque altro simbolo indica esattamente quel simbolo\n"
|
||||
"\n"
|
||||
"\n"
|
||||
|
||||
#: albatrobot.py:458
|
||||
msgid "You can roll up to 500 dice with up to 500 faces at once!"
|
||||
msgstr "Puoi tirare al massimo 1000 dadi con al massimo 1000 facce per volta!"
|
||||
|
||||
#: albatrobot.py:465
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {rolls} = {result}"
|
||||
msgstr "{user_name} ha tirato {rolls} = {result}"
|
||||
|
||||
#: albatrobot.py:473
|
||||
msgid "You used a wrong syntax!"
|
||||
msgstr "Hai usato una sintassi errata!"
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# Albatrobot translation.
|
||||
# Copyright (C) 2021 bursa-pastoris
|
||||
# Translation template for Albatrobot.
|
||||
# Copyright (C) 2021, 2023 bursa-pastoris
|
||||
# This file is distributed under the same license as the Albatrobot package.
|
||||
# bursa-pastoris <bursapastoris at disroot dot org>, 2021.
|
||||
# bursa-pastoris <bursapastoris at disroot dot org>, 2021, 2023.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.2\n"
|
||||
"Project-Id-Version: Albatrobot 0.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-01 19:49+0100\n"
|
||||
"POT-Creation-Date: 2023-03-23 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"
|
||||
|
@ -17,47 +17,55 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: main.py:68 main.py:72 main.py:76
|
||||
#: main.py:48 main.py:60 albatrobot.py:161
|
||||
msgid "stop"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:79
|
||||
#: main.py:48 main.py:69 albatrobot.py:159
|
||||
msgid "init"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:65 albatrobot.py:143
|
||||
msgid "help"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:82
|
||||
#: main.py:73 albatrobot.py:144
|
||||
msgid "legal"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:85
|
||||
#: main.py:77 albatrobot.py:148
|
||||
msgid "version"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:90
|
||||
msgid "roll"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:95
|
||||
#: main.py:84 albatrobot.py:136
|
||||
msgid "christmas"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:98
|
||||
#: main.py:88 albatrobot.py:137
|
||||
msgid "easter"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:101
|
||||
#: main.py:92
|
||||
msgid "end_of_year_dinner"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:73
|
||||
#: main.py:96 albatrobot.py:141
|
||||
msgid "epiphony"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:100 albatrobot.py:145 albatrobot.py:196
|
||||
msgid "roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:81
|
||||
msgid "User not authorized."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:80
|
||||
#: albatrobot.py:89
|
||||
msgid "Unknown command."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:94
|
||||
#: albatrobot.py:103
|
||||
msgid ""
|
||||
"Hello, I'm Albatrobot\\! Type \\/help for help\\!\n"
|
||||
"\n"
|
||||
|
@ -69,49 +77,139 @@ msgid ""
|
|||
msgstr ""
|
||||
|
||||
#: albatrobot.py:121
|
||||
msgid "Albatrobot will be stopped in a few moments."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:134
|
||||
msgid "*Albatrobot commands*"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:136
|
||||
msgid "get an image"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid " `\\(\\<author\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:137
|
||||
msgid ""
|
||||
"*Officially supported commands*\n"
|
||||
"/help: show officialy supported commands\n"
|
||||
"/legal: send legal info about privacy and copyright\n"
|
||||
"/roll: roll dice following DnD notation\n"
|
||||
"\n"
|
||||
"Admins can also use /stop to stop the bot from Telegram in case of emergency"
|
||||
"\\."
|
||||
"get a citation from given author, or a random one if no author is "
|
||||
"specified\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:154
|
||||
msgid "*Albatrobot's version:* {}'"
|
||||
#: albatrobot.py:139
|
||||
msgid "end\\_of\\_year\\_dinner"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:174
|
||||
msgid "{}, you can roll at most 1000 dice with at most 1000 faces at once!"
|
||||
#: albatrobot.py:139
|
||||
msgid "send all citations in the archive to a random user\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:187
|
||||
#: albatrobot.py:141
|
||||
msgid " `\\(\\<text\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:141
|
||||
msgid "get a voice message from Albatrobot reading given text"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:143
|
||||
msgid "get help about bot commands\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:144
|
||||
msgid "get legal info about privacy and copyright\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid " `\\<dice\\>\\(\\<modifiers\\>\\)`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:145
|
||||
msgid ""
|
||||
"roll dice, with or without modifiers\\. Launch without arguments to get help "
|
||||
"on the modifiers\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:148
|
||||
msgid "get Albatrobot's version"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:157
|
||||
msgid "*Additional commands for bot admins*"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:159
|
||||
msgid ""
|
||||
"reinitialize the bot\\. This updates inline suggestions and purges /epiphony "
|
||||
"cached files\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:161
|
||||
msgid ""
|
||||
"stop Albatrobot\\. To be used _only_ in case of emergency: restarting the "
|
||||
"bot after this command is used requires direct access to the server\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:173
|
||||
msgid ""
|
||||
"Bot admins have access to additional commands and can get help on them "
|
||||
"using /help in a private chat\\."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:182
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {result}"
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{acom_help}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:188
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{ccom_help}\n"
|
||||
"\n"
|
||||
"{non_admin_addendum}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:196
|
||||
msgid "Roll dice"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:199
|
||||
msgid "Initializing..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:203
|
||||
msgid ""
|
||||
"Christmas is good, but you can't invoke it so often! Try again in some "
|
||||
"seconds...'"
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:233
|
||||
#: albatrobot.py:228
|
||||
msgid "*Albatrobot's version:* {}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:242
|
||||
msgid ""
|
||||
"Christmas is good, but you can't invoke it so often! Try again in some "
|
||||
"seconds..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:273
|
||||
msgid ""
|
||||
"Easter is good, but you can't invoke it so often! Retry in some seconds..."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:250
|
||||
#: albatrobot.py:296
|
||||
msgid "The most similar is {}."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:252
|
||||
msgid "The most similar are {}."
|
||||
#: albatrobot.py:300
|
||||
msgid "The most similar are {} and {}."
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:260
|
||||
#: albatrobot.py:304
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Author {required_author} is not present in the archive! {suggestion}\n"
|
||||
|
@ -119,11 +217,170 @@ msgid ""
|
|||
"N.b.: CaSe Is ImPoRtAnT!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:295
|
||||
msgid "Hello there, here an end of year dinner offered by {} !"
|
||||
#: albatrobot.py:324 albatrobot.py:352
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{cit}\n"
|
||||
"\n"
|
||||
"~{author}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:309
|
||||
#: albatrobot.py:345
|
||||
msgid "Hello there, here an end of year dinner offered by {}!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:359
|
||||
#, python-brace-format
|
||||
msgid "End of year dinner of {cits_number} plates delvered to {dest}!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:372
|
||||
msgid "What should I say?"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:402
|
||||
msgid "add `n` to the roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:403
|
||||
msgid "subtract `n` from the roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:404
|
||||
msgid "multiply the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:405
|
||||
msgid "divide the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:406
|
||||
msgid "floor divide the roll by `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:407
|
||||
msgid "exponentiate the roll to `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:408
|
||||
msgid "keep highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:409
|
||||
msgid "keep lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:410
|
||||
msgid "drop highest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:411
|
||||
msgid "drop lowest `n` rolls \\(defaults to `1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:412
|
||||
msgid "explode rolls equal to `n` \\(defaults to die size\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:413
|
||||
msgid "explode rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:414
|
||||
msgid "explode rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:415
|
||||
msgid "count failures as rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:416
|
||||
msgid "count failures as rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:417
|
||||
msgid "count successes as rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:418
|
||||
msgid "count successes as rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:419
|
||||
msgid "penetrate rolls equal to dice size"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:420
|
||||
msgid "penetrate rolls lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:421
|
||||
msgid "penetrate rolls higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:422
|
||||
msgid "add `n` to each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:423
|
||||
msgid "subtract `n` from each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:424
|
||||
msgid "multiply by `n` each roll"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:425
|
||||
msgid ""
|
||||
"reroll each dice that rolled `n` until there are no `n` left\\(defaults to "
|
||||
"`1`\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:427
|
||||
msgid "reroll once each dice that rolled `n` \\(defaults to 1\\)"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:429
|
||||
msgid ""
|
||||
"reroll dice that rolled lower than `n` until there are no results lower than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:431
|
||||
msgid ""
|
||||
"reroll dice that rolled higher than `n` until there no results higher than "
|
||||
"`n` left"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:433
|
||||
msgid "reroll once dice that roll lower than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:434
|
||||
msgid "reroll once dice that roll higher than `n`"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:439
|
||||
msgid ""
|
||||
"To roll, use the syntax `\\<number of dice\\>d\\<dice size\\>`\\. You can "
|
||||
"also use the following modifiers\\. Note that:\n"
|
||||
"\\- `\\(n\\)` means that an integer number may be specified, but if it isn't "
|
||||
"the specified default value is assumed\n"
|
||||
"\\- `n` means that an integer number is required\n"
|
||||
"\\- any other symbol means exactly that symbol\n"
|
||||
"\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:458
|
||||
msgid "You can roll up to 500 dice with up to 500 faces at once!"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:465
|
||||
#, python-brace-format
|
||||
msgid "{user_name} rolled {rolls} = {result}"
|
||||
msgstr ""
|
||||
|
||||
#: albatrobot.py:473
|
||||
msgid "You used a wrong syntax!"
|
||||
msgstr ""
|
||||
|
|
149
main.py
149
main.py
|
@ -19,90 +19,93 @@
|
|||
along with Albatrobot. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import configparser
|
||||
from datetime import timedelta
|
||||
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
|
||||
import gettext
|
||||
from telegram import Update
|
||||
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
|
||||
|
||||
import albatrobot
|
||||
|
||||
# Settings
|
||||
config = configparser.ConfigParser()
|
||||
config.read("settings.ini")
|
||||
token = config["Bot"]["Token"]
|
||||
for var in config["Settings"]["Users"].split(","):
|
||||
albatrobot.users.append(int(var))
|
||||
for var in config["Settings"]["Admins"].split(","):
|
||||
albatrobot.admins.append(int(var))
|
||||
albatrobot.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"]
|
||||
|
||||
from constants import *
|
||||
|
||||
# Localization
|
||||
import gettext
|
||||
|
||||
transl = gettext.translation("messages", localedir="locales",languages=[lang])
|
||||
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
|
||||
if PROXY != '':
|
||||
application = ApplicationBuilder().token(TOKEN).get_updates_proxy_url(PROXY).build()
|
||||
else:
|
||||
application = ApplicationBuilder().token(TOKEN).build()
|
||||
|
||||
# Bot building - Unauthorized users filtering
|
||||
dispatcher.add_handler(
|
||||
MessageHandler(
|
||||
Filters.command and (~Filters.user(albatrobot.users)),
|
||||
albatrobot.unauthorized_user))
|
||||
|
||||
# Bot building - Basic functions
|
||||
dispatcher.add_handler(
|
||||
CommandHandler("start",
|
||||
albatrobot.start))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["stop",_("stop")],
|
||||
albatrobot.unauthorized_user,
|
||||
(~Filters.user(albatrobot.admins))))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["stop",_("stop")],
|
||||
albatrobot.stop,
|
||||
Filters.user(albatrobot.admins)))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["stop",_("stop")],
|
||||
albatrobot.unauthorized_user))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["help",_("help")],
|
||||
albatrobot.help))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["legal",_("legal")],
|
||||
albatrobot.legal))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["version",_("version")],
|
||||
albatrobot.version))
|
||||
# Filter unauthorized users
|
||||
application.add_handler(
|
||||
MessageHandler((~filters.User(USERS)),
|
||||
albatrobot.unauthorized_user)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['stop',_('stop'),'init',_('init')],
|
||||
albatrobot.unauthorized_user,
|
||||
(~filters.User(ADMINS)))
|
||||
)
|
||||
|
||||
# Bot building - Commands
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["roll",_("roll")],
|
||||
albatrobot.roll))
|
||||
|
||||
# Bot building - Easter eggs
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["christmas",_("christmas")],
|
||||
albatrobot.christmas))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["easter",_("easter")],
|
||||
albatrobot.easter))
|
||||
dispatcher.add_handler(
|
||||
CommandHandler(["end_of_year_dinner",_("end_of_year_dinner")],
|
||||
albatrobot.end_of_year_dinner))
|
||||
dispatcher.add_handler(
|
||||
MessageHandler(Filters.command,
|
||||
albatrobot.unknown_command))
|
||||
# Basic functions
|
||||
application.add_handler(
|
||||
CommandHandler('start',
|
||||
albatrobot.start)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['stop',_('stop')],
|
||||
albatrobot.stop,
|
||||
filters.User(ADMINS))
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['help',_('help')],
|
||||
albatrobot.help)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['init',_('init')],
|
||||
albatrobot.init)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['legal',_('legal')],
|
||||
albatrobot.legal)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['version',_('version')],
|
||||
albatrobot.version)
|
||||
)
|
||||
|
||||
|
||||
# Commands
|
||||
application.add_handler(
|
||||
CommandHandler(['christmas',_('christmas')],
|
||||
albatrobot.christmas)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['easter',_('easter')],
|
||||
albatrobot.easter)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['end_of_year_dinner',_('end_of_year_dinner')],
|
||||
albatrobot.end_of_year_dinner)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['epiphony',_('epiphony')],
|
||||
albatrobot.epiphony)
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler(['roll',_('roll')],
|
||||
albatrobot.roll)
|
||||
)
|
||||
|
||||
# Unknown commands
|
||||
application.add_handler(
|
||||
MessageHandler(filters.COMMAND,
|
||||
albatrobot.unknown_command)
|
||||
)
|
||||
|
||||
# Start bot
|
||||
updater.start_polling()
|
||||
application.run_polling()
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
python-telegram-bot==13.0
|
||||
python-Levenshtein==0.12.2
|
||||
psutil==5.9.0
|
||||
python-telegram-bot[socks]~=20.0
|
||||
|
||||
psutil~=5.9
|
||||
py-rolldice~=0.4
|
||||
python-Levenshtein~=0.12
|
||||
pyttsx3~=2.90
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ID,author,text,year,place
|
||||
ABT-0001,Albatrobot,No citations available,2022,
|
||||
ABT-0002,Albatrobot,"No citations available, I said!",,Brescia
|
|
|
@ -1 +0,0 @@
|
|||
citations = {'Albatrobot':['no citations available']}
|
Binary file not shown.
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 242 KiB |
|
@ -1,71 +0,0 @@
|
|||
<?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>
|
Before Width: | Height: | Size: 2.8 KiB |
|
@ -1,13 +1,18 @@
|
|||
[Bot]
|
||||
Token:
|
||||
|
||||
[Settings]
|
||||
[Users]
|
||||
Users:
|
||||
Admins:
|
||||
|
||||
[Settings]
|
||||
Language:
|
||||
MsgCooldown: 5
|
||||
Proxy:
|
||||
YellLang:
|
||||
YellSpeed: 120
|
||||
|
||||
[Resources]
|
||||
CitePath: resources/citations
|
||||
CitArchive: resources/citations/citations.csv
|
||||
ImgPath: resources/imgs
|
||||
|
||||
|
|
Loading…
Reference in New Issue