Source code for telegram_game.redis_game

"""
In some games the game state is not critical, it is ok to drop all and ask the
player to start from scratch. In that case just use
:py:class:`telegram_game.game.BaseGame`, and store state in the `start` coroutine
local variables or `self` attributes.

But in some games it is crucial to save the state
between different bot runs. In that case you can use :py:class:`RedisGame`.
"""

import os

import redis
import msgpack

from telegram_game.game import BaseGame


[docs]class RedisGame(BaseGame): """Game with persistent fields stored in Redis. You can define persistent fields with telegram_game.redis_game.RedisField, and use them as the usual variables in your game logic code: .. code-block:: python class Game(RedisGame): score = RedisField(initial=0) level = RedisField(initial=1) async def start(self): self.score += 1 await self.reply(self.level) Field values are serialised with `msgpack`_. Think of it like about a trivial ORM. .. note:: It is recommened to run the redis server on the same host, where the game bot is running (to reduce latency). In this case it should be ok to use a blocking redis client for the sake of simplicity. .. _msgpack: http://msgpack.org/ """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'REDIS_URL' in os.environ: self.redis = redis.from_url(os.environ['REDIS_URL']) else: self.redis = redis.Redis() def __getattribute__(self, key): value = super().__getattribute__(key) if isinstance(value, RedisField): ret = self.redis.get('tg:%s:%s' % (self.chat_id, key)) if ret is None: return value.initial return msgpack.loads(ret) return value def __setattr__(self, key, value): sup = super() if hasattr(self, key) and isinstance(sup.__getattribute__(key), RedisField): self.redis.set('tg:%s:%s' % (self.chat_id, key), msgpack.dumps(value)) else: sup.__setattr__(key, value)
[docs]class RedisField: """Define a `RedisGame` field as a persistent field.""" def __init__(self, initial): self.initial = initial