python-telegram-bot v7 compatability + more (#35)
* Remove import of telegram.Emoji * Using a list of filters in MessageHandler is getting deprecated * Update requirements Proudly upgrades to latest python-telegram-bot * Refine readme * Test kill command * Another implement * Add /kill command * initial config support * json config * Add token into json * Typo * Add Admin list * Refine admin & starter * Fix an exception * Fix typo * refine readme * Update help
This commit is contained in:
parent
8be4bc688d
commit
783e010956
12 changed files with 113 additions and 84 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
|||
# Config file
|
||||
config.json
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
17
README.md
17
README.md
|
@ -2,18 +2,19 @@
|
|||
|
||||
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](./LICENSE)
|
||||
|
||||
Telegram Bot that allows you to play the popular card game UNO via inline queries. The bot currently runs as [@unobot](http://telegram.me/unobot)
|
||||
Telegram Bot that allows you to play the popular card game UNO via inline queries. The bot currently runs as [@unobot](http://telegram.me/unobot).
|
||||
|
||||
To run the bot yourself, you will need:
|
||||
- Python (tested with 3.4 and 3.5)
|
||||
- The [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) module version 5.0.0
|
||||
- Python (tested with 3.4+)
|
||||
- The [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) module
|
||||
- [Pony ORM](https://ponyorm.com/)
|
||||
|
||||
## Setup
|
||||
- Get a bot token from [@BotFather](http://telegram.me/BotFather) and place it in `credentials.py`
|
||||
- Convert all language files from .po to .mo using `msgfmt unobot.po -o unobot.mo`
|
||||
- Use `/setinline` and `/setinlinefeedback` with BotFather for your bot
|
||||
- Get a bot token from [@BotFather](http://telegram.me/BotFather) and change configurations in `config.json`.
|
||||
- Convert all language files from `.po` files to `.mo` files using `msgfmt unobot.po -o unobot.mo`.
|
||||
Also try `find . -maxdepth 2 -type d -name 'LC_MESSAGES' -exec bash -c 'msgfmt {}/unobot.po -o {}/unobot.mo' \;`.
|
||||
- Use `/setinline` and `/setinlinefeedback` with BotFather for your bot.
|
||||
|
||||
Then run the bot with `python3 bot.py`
|
||||
Then run the bot with `python3 bot.py`.
|
||||
|
||||
Code documentation is minimal but there
|
||||
Code documentation is minimal but there.
|
||||
|
|
71
bot.py
71
bot.py
|
@ -17,7 +17,6 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from random import randint
|
||||
|
@ -45,13 +44,16 @@ import settings
|
|||
|
||||
from simple_commands import help
|
||||
|
||||
#import json
|
||||
#with open("config.json","r") as f:
|
||||
# config = json.loads(f.read())
|
||||
#forbidden = config.get("black_list", None)
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@user_locale
|
||||
def notify_me(bot, update):
|
||||
"""Handler for /notify_me command, pm people for next game"""
|
||||
|
@ -88,7 +90,8 @@ def new_game(bot, update):
|
|||
del gm.remind_dict[update.message.chat_id]
|
||||
|
||||
game = gm.new_game(update.message.chat)
|
||||
game.owner = update.message.from_user
|
||||
game.starter = update.message.from_user
|
||||
game.owner.append(update.message.from_user.id)
|
||||
send_async(bot, chat_id,
|
||||
text=_("Created a new game! Join the game with /join "
|
||||
"and start the game with /start"))
|
||||
|
@ -96,6 +99,41 @@ def new_game(bot, update):
|
|||
if botan:
|
||||
botan.track(update.message, 'New games')
|
||||
|
||||
@user_locale
|
||||
def kill_game(bot, update):
|
||||
"""Handler for the /kill command"""
|
||||
chat = update.message.chat
|
||||
user = update.message.from_user
|
||||
games = gm.chatid_games.get(chat.id)
|
||||
|
||||
if update.message.chat.type == 'private':
|
||||
help(bot, update)
|
||||
return
|
||||
|
||||
if not games:
|
||||
send_async(bot, chat.id,
|
||||
text=_("There is no running game in this chat."))
|
||||
return
|
||||
|
||||
game = games[-1]
|
||||
|
||||
if user.id in game.owner:
|
||||
|
||||
try:
|
||||
gm.end_game(chat, user)
|
||||
send_async(bot, chat.id, text=__("Game ended!", multi=game.translate))
|
||||
|
||||
except NoGameInChatError:
|
||||
send_async(bot, chat.id,
|
||||
text=_("The game is not started yet. "
|
||||
"Join the game with /join and start the game with /start"),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
|
||||
else:
|
||||
send_async(bot, chat.id,
|
||||
text=_("Only the game creator ({name}) and admin can do that.")
|
||||
.format(name=game.starter.first_name),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
|
||||
@user_locale
|
||||
def join_game(bot, update):
|
||||
|
@ -321,7 +359,7 @@ def close_game(bot, update):
|
|||
|
||||
game = games[-1]
|
||||
|
||||
if game.owner.id == user.id:
|
||||
if user.id in game.owner:
|
||||
game.open = False
|
||||
send_async(bot, chat.id, text=_("Closed the lobby. "
|
||||
"No more players can join this game."))
|
||||
|
@ -329,8 +367,8 @@ def close_game(bot, update):
|
|||
|
||||
else:
|
||||
send_async(bot, chat.id,
|
||||
text=_("Only the game creator ({name}) can do that.")
|
||||
.format(name=game.owner.first_name),
|
||||
text=_("Only the game creator ({name}) and admin can do that.")
|
||||
.format(name=game.starter.first_name),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
return
|
||||
|
||||
|
@ -349,15 +387,15 @@ def open_game(bot, update):
|
|||
|
||||
game = games[-1]
|
||||
|
||||
if game.owner.id == user.id:
|
||||
if user.id in game.owner:
|
||||
game.open = True
|
||||
send_async(bot, chat.id, text=_("Opened the lobby. "
|
||||
"New players may /join the game."))
|
||||
return
|
||||
else:
|
||||
send_async(bot, chat.id,
|
||||
text=_("Only the game creator ({name}) can do that")
|
||||
.format(name=game.owner.first_name),
|
||||
text=_("Only the game creator ({name}) and admin can do that.")
|
||||
.format(name=game.starter.first_name),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
return
|
||||
|
||||
|
@ -376,7 +414,7 @@ def enable_translations(bot, update):
|
|||
|
||||
game = games[-1]
|
||||
|
||||
if game.owner.id == user.id:
|
||||
if user.id in game.owner:
|
||||
game.translate = True
|
||||
send_async(bot, chat.id, text=_("Enabled multi-translations. "
|
||||
"Disable with /disable_translations"))
|
||||
|
@ -384,8 +422,8 @@ def enable_translations(bot, update):
|
|||
|
||||
else:
|
||||
send_async(bot, chat.id,
|
||||
text=_("Only the game creator ({name}) can do that")
|
||||
.format(name=game.owner.first_name),
|
||||
text=_("Only the game creator ({name}) and admin can do that.")
|
||||
.format(name=game.starter.first_name),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
return
|
||||
|
||||
|
@ -404,7 +442,7 @@ def disable_translations(bot, update):
|
|||
|
||||
game = games[-1]
|
||||
|
||||
if game.owner.id == user.id:
|
||||
if user.id in game.owner:
|
||||
game.translate = False
|
||||
send_async(bot, chat.id, text=_("Disabled multi-translations. "
|
||||
"Enable them again with "
|
||||
|
@ -413,8 +451,8 @@ def disable_translations(bot, update):
|
|||
|
||||
else:
|
||||
send_async(bot, chat.id,
|
||||
text=_("Only the game creator ({name}) can do that")
|
||||
.format(name=game.owner.first_name),
|
||||
text=_("Only the game creator ({name}) and admin can do that.")
|
||||
.format(name=game.starter.first_name),
|
||||
reply_to_message_id=update.message.message_id)
|
||||
return
|
||||
|
||||
|
@ -728,6 +766,7 @@ dispatcher.add_handler(ChosenInlineResultHandler(process_result))
|
|||
dispatcher.add_handler(CallbackQueryHandler(select_game))
|
||||
dispatcher.add_handler(CommandHandler('start', start_game, pass_args=True))
|
||||
dispatcher.add_handler(CommandHandler('new', new_game))
|
||||
dispatcher.add_handler(CommandHandler('kill', kill_game))
|
||||
dispatcher.add_handler(CommandHandler('join', join_game))
|
||||
dispatcher.add_handler(CommandHandler('leave', leave_game))
|
||||
dispatcher.add_handler(CommandHandler('open', open_game))
|
||||
|
@ -740,7 +779,7 @@ dispatcher.add_handler(CommandHandler('skip', skip_player))
|
|||
dispatcher.add_handler(CommandHandler('notify_me', notify_me))
|
||||
simple_commands.register()
|
||||
settings.register()
|
||||
dispatcher.add_handler(MessageHandler([Filters.status_update], status_update))
|
||||
dispatcher.add_handler(MessageHandler(Filters.status_update, status_update))
|
||||
dispatcher.add_error_handler(error)
|
||||
|
||||
start_bot(updater)
|
||||
|
|
10
card.py
10
card.py
|
@ -18,8 +18,6 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from telegram.emoji import Emoji
|
||||
|
||||
# Colors
|
||||
RED = 'r'
|
||||
BLUE = 'b'
|
||||
|
@ -30,10 +28,10 @@ BLACK = 'x'
|
|||
COLORS = (RED, BLUE, GREEN, YELLOW)
|
||||
|
||||
COLOR_ICONS = {
|
||||
RED: Emoji.HEAVY_BLACK_HEART,
|
||||
BLUE: Emoji.BLUE_HEART,
|
||||
GREEN: Emoji.GREEN_HEART,
|
||||
YELLOW: Emoji.YELLOW_HEART,
|
||||
RED: '❤️',
|
||||
BLUE: '💙',
|
||||
GREEN: '💚',
|
||||
YELLOW: '💛',
|
||||
BLACK: '⬛️'
|
||||
}
|
||||
|
||||
|
|
8
config.json.example
Normal file
8
config.json.example
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"token": "token_here",
|
||||
"botan_token": null,
|
||||
"admin_list": [0],
|
||||
"open_lobby": true,
|
||||
"enable_translations": false,
|
||||
"workers": 32
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Telegram bot to play UNO in group chats
|
||||
# Copyright (c) 2016 Jannes Höke <uno@jhoeke.de>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
TOKEN = 'TOKEN'
|
||||
BOTAN_TOKEN = '' # Optional: Add a botan.io token if you want bot statistics
|
14
game.py
14
game.py
|
@ -19,23 +19,25 @@
|
|||
|
||||
|
||||
import logging
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from deck import Deck
|
||||
import card as c
|
||||
|
||||
|
||||
class Game(object):
|
||||
""" This class represents a game of UNO """
|
||||
current_player = None
|
||||
reversed = False
|
||||
draw_counter = 0
|
||||
choosing_color = False
|
||||
started = False
|
||||
owner = None
|
||||
open = True
|
||||
translate = False
|
||||
draw_counter = 0
|
||||
players_won = 0
|
||||
starter = None
|
||||
with open("config.json","r") as f:
|
||||
config = json.loads(f.read())
|
||||
owner = config.get("admin_list", None)
|
||||
open = config.get("open_lobby", True)
|
||||
translate = config.get("enable_translations", False)
|
||||
|
||||
def __init__(self, chat):
|
||||
self.chat = chat
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
python-telegram-bot==5
|
||||
python-telegram-bot
|
||||
pony
|
||||
|
|
22
settings.py
22
settings.py
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from telegram import ReplyKeyboardMarkup, Emoji
|
||||
from telegram import ReplyKeyboardMarkup
|
||||
from telegram.ext import CommandHandler, RegexHandler
|
||||
|
||||
from utils import send_async
|
||||
|
@ -44,12 +44,12 @@ def show_settings(bot, update):
|
|||
us = UserSetting(id=update.message.from_user.id)
|
||||
|
||||
if not us.stats:
|
||||
stats = Emoji.BAR_CHART + ' ' + _("Enable statistics")
|
||||
stats = '📊' + ' ' + _("Enable statistics")
|
||||
else:
|
||||
stats = Emoji.CROSS_MARK + ' ' + _("Delete all statistics")
|
||||
stats = '❌' + ' ' + _("Delete all statistics")
|
||||
|
||||
kb = [[stats], [Emoji.EARTH_GLOBE_EUROPE_AFRICA + ' ' + _("Language")]]
|
||||
send_async(bot, chat.id, text=Emoji.WRENCH + ' ' + _("Settings"),
|
||||
kb = [[stats], ['🌍' + ' ' + _("Language")]]
|
||||
send_async(bot, chat.id, text='🔧' + ' ' + _("Settings"),
|
||||
reply_markup=ReplyKeyboardMarkup(keyboard=kb,
|
||||
one_time_keyboard=True))
|
||||
|
||||
|
@ -60,12 +60,12 @@ def kb_select(bot, update, groups):
|
|||
user = update.message.from_user
|
||||
option = groups[0]
|
||||
|
||||
if option == Emoji.BAR_CHART:
|
||||
if option == '📊':
|
||||
us = UserSetting.get(id=user.id)
|
||||
us.stats = True
|
||||
send_async(bot, chat.id, text=_("Enabled statistics!"))
|
||||
|
||||
elif option == Emoji.EARTH_GLOBE_EUROPE_AFRICA:
|
||||
elif option == '🌍':
|
||||
kb = [[locale + ' - ' + descr]
|
||||
for locale, descr
|
||||
in sorted(available_locales.items())]
|
||||
|
@ -73,7 +73,7 @@ def kb_select(bot, update, groups):
|
|||
reply_markup=ReplyKeyboardMarkup(keyboard=kb,
|
||||
one_time_keyboard=True))
|
||||
|
||||
elif option == Emoji.CROSS_MARK:
|
||||
elif option == '❌':
|
||||
us = UserSetting.get(id=user.id)
|
||||
us.stats = False
|
||||
us.first_places = 0
|
||||
|
@ -98,9 +98,9 @@ def locale_select(bot, update, groups):
|
|||
|
||||
def register():
|
||||
dispatcher.add_handler(CommandHandler('settings', show_settings))
|
||||
dispatcher.add_handler(RegexHandler('^([' + Emoji.BAR_CHART +
|
||||
Emoji.EARTH_GLOBE_EUROPE_AFRICA +
|
||||
Emoji.CROSS_MARK + ']) .+$',
|
||||
dispatcher.add_handler(RegexHandler('^([' + '📊' +
|
||||
'🌍' +
|
||||
'❌' + ']) .+$',
|
||||
kb_select, pass_groups=True))
|
||||
dispatcher.add_handler(RegexHandler(r'^(\w\w_\w\w) - .*',
|
||||
locale_select, pass_groups=True))
|
||||
|
|
|
@ -17,21 +17,20 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import json
|
||||
from telegram.ext import Updater
|
||||
from telegram.contrib.botan import Botan
|
||||
|
||||
from game_manager import GameManager
|
||||
from database import db
|
||||
from credentials import TOKEN, BOTAN_TOKEN
|
||||
|
||||
db.bind('sqlite', 'uno.sqlite3', create_db=True)
|
||||
db.generate_mapping(create_tables=True)
|
||||
|
||||
gm = GameManager()
|
||||
updater = Updater(token=TOKEN, workers=32)
|
||||
with open("config.json","r") as f:
|
||||
config = json.loads(f.read())
|
||||
updater = Updater(token=config.get("token"), workers=config.get("workers", 32))
|
||||
dispatcher = updater.dispatcher
|
||||
|
||||
botan = False
|
||||
if BOTAN_TOKEN:
|
||||
botan = Botan(BOTAN_TOKEN)
|
||||
botan = Botan(config.get("botan_token", None))
|
||||
|
|
|
@ -46,6 +46,7 @@ help_text = ("Follow these steps:\n\n"
|
|||
"Other commands (only game creator):\n"
|
||||
"/close - Close lobby\n"
|
||||
"/open - Open lobby\n"
|
||||
"/kill - Terminate the game\n"
|
||||
"/enable_translations - Translate relevant texts into all "
|
||||
"languages spoken in a game\n"
|
||||
"/disable_translations - Use English for those texts\n\n"
|
||||
|
|
17
utils.py
17
utils.py
|
@ -20,7 +20,6 @@
|
|||
|
||||
import logging
|
||||
|
||||
from telegram import Emoji
|
||||
from telegram.ext.dispatcher import run_async
|
||||
|
||||
from internationalization import _, __
|
||||
|
@ -51,29 +50,29 @@ def display_name(user):
|
|||
def display_color(color):
|
||||
""" Convert a color code to actual color name """
|
||||
if color == "r":
|
||||
return _("{emoji} Red").format(emoji=Emoji.HEAVY_BLACK_HEART)
|
||||
return _("{emoji} Red").format(emoji='❤️')
|
||||
if color == "b":
|
||||
return _("{emoji} Blue").format(emoji=Emoji.BLUE_HEART)
|
||||
return _("{emoji} Blue").format(emoji='💙')
|
||||
if color == "g":
|
||||
return _("{emoji} Green").format(emoji=Emoji.GREEN_HEART)
|
||||
return _("{emoji} Green").format(emoji='💚')
|
||||
if color == "y":
|
||||
return _("{emoji} Yellow").format(emoji=Emoji.YELLOW_HEART)
|
||||
return _("{emoji} Yellow").format(emoji='💛')
|
||||
|
||||
|
||||
def display_color_group(color, game):
|
||||
""" Convert a color code to actual color name """
|
||||
if color == "r":
|
||||
return __("{emoji} Red", game.translate).format(
|
||||
emoji=Emoji.HEAVY_BLACK_HEART)
|
||||
emoji='❤️')
|
||||
if color == "b":
|
||||
return __("{emoji} Blue", game.translate).format(
|
||||
emoji=Emoji.BLUE_HEART)
|
||||
emoji='💙')
|
||||
if color == "g":
|
||||
return __("{emoji} Green", game.translate).format(
|
||||
emoji=Emoji.GREEN_HEART)
|
||||
emoji='💚')
|
||||
if color == "y":
|
||||
return __("{emoji} Yellow", game.translate).format(
|
||||
emoji=Emoji.YELLOW_HEART)
|
||||
emoji='💛')
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
|
|
Loading…
Reference in a new issue