diff --git a/.gitignore b/.gitignore
index 49decb9..9881366 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# Config file
+config.json
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/README.md b/README.md
index a7aa7ea..e00fa3d 100644
--- a/README.md
+++ b/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.
diff --git a/bot.py b/bot.py
index e997a60..996a4ed 100644
--- a/bot.py
+++ b/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 .
-
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)
diff --git a/card.py b/card.py
index 93d8eb6..20717d8 100644
--- a/card.py
+++ b/card.py
@@ -18,8 +18,6 @@
# along with this program. If not, see .
-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: 'β¬οΈ'
}
diff --git a/config.json.example b/config.json.example
new file mode 100644
index 0000000..0a851a0
--- /dev/null
+++ b/config.json.example
@@ -0,0 +1,8 @@
+{
+ "token": "token_here",
+ "botan_token": null,
+ "admin_list": [0],
+ "open_lobby": true,
+ "enable_translations": false,
+ "workers": 32
+}
diff --git a/credentials.py b/credentials.py
deleted file mode 100644
index 3b4386d..0000000
--- a/credentials.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python3
-#
-# Telegram bot to play UNO in group chats
-# Copyright (c) 2016 Jannes HΓΆke
-#
-# 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 .
-
-
-TOKEN = 'TOKEN'
-BOTAN_TOKEN = '' # Optional: Add a botan.io token if you want bot statistics
diff --git a/game.py b/game.py
index 5308fac..b2981c8 100644
--- a/game.py
+++ b/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
diff --git a/requirements.txt b/requirements.txt
index ae6b2e0..76cafa0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-python-telegram-bot==5
+python-telegram-bot
pony
diff --git a/settings.py b/settings.py
index d10bf38..622d897 100644
--- a/settings.py
+++ b/settings.py
@@ -18,7 +18,7 @@
# along with this program. If not, see .
-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))
diff --git a/shared_vars.py b/shared_vars.py
index 526620d..e966c55 100644
--- a/shared_vars.py
+++ b/shared_vars.py
@@ -17,21 +17,20 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
+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))
diff --git a/simple_commands.py b/simple_commands.py
index 3c032a0..2aad221 100644
--- a/simple_commands.py
+++ b/simple_commands.py
@@ -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"
diff --git a/utils.py b/utils.py
index 134a1b0..2c5f288 100644
--- a/utils.py
+++ b/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):