Skip to content

Commit

Permalink
add code
Browse files Browse the repository at this point in the history
  • Loading branch information
algroznykh committed Mar 2, 2017
1 parent 04cacf8 commit f6f9e7f
Show file tree
Hide file tree
Showing 14 changed files with 633 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# f11nbot
a telegram bot for supporting offline conversations

## run migrations

`python manage.py migrate`

## run commands for creating games and promos

`python manage.py generate_games`
`python manage.py generate_promo`
21 changes: 21 additions & 0 deletions const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# coding=utf-8
"""
Строковые константы для кнопок
"""

EXIT_CMD = u"Выход"
GAME_OVER_MSG = u"Игра окончена"

ASK_FOR_PROMO_MSG = u"Чтобы продолжить, введите сюда промокод, который выдал вам таймпад при покупке билета"
NEXT_QUESTION_CMD = u"Следующий вопрос"
BACK_CMD = u"Назад"
START_MSG = u"Приветствую! Это бот Факторизация отношений"
PARTNER_MSG = u"Ваш партнер уже тут, и вы в игре"
SEND_PROMO_MSG = u"Это игра на двоих! Перешлите промокод своему партнеру. Когда он/она присоединится, начнется игра"
NO_SUCH_GAME_MSG = u"Такой игры нет!"
HELP_MSG = START_MSG # TODO make help
WAIT_FOR_PARTNER_MSG = u'Чтобы двинуться дальше, ваш партнер должен тоже нажать "Следующий вопрос"'
FINISH_FACT_CMD = u'Закончить факторизацию'
START_CMD = u'Начать'
NO_MORE_QUESTIONS_MSG = u'Вопросов в этой категории больше нет'
SELECT_CATEGORY_MSG = u"Для начала выберите фактор, который хотели бы обсудить подробнее. Внимание! Игра не начнется пока оба партнера не выберут один и тот же фактор"
Empty file added data/__init__.py
Empty file.
Empty file added data/management/__init__.py
Empty file.
Empty file.
54 changes: 54 additions & 0 deletions data/management/commands/generate_games.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
import os
import csv
from django.core.management.base import BaseCommand, CommandError
from data.models import Game, Discussion, QuestionCategory, Question

from collections import OrderedDict


class Command(BaseCommand):
help = 'Creates StVal Discussion + Promos'

@staticmethod
def read_questions(path):
with open(path, 'r') as f:
reader = csv.reader(f)
return {int(l[0]): [l[1], int(l[2])] for l in reader}

@staticmethod
def read_categories(path):
with open(path, 'r') as f:
reader = csv.reader(f)
return OrderedDict([(int(l[0]), l[1]) for l in reader])

def handle(self, *args, **options):
discussion, created = Discussion.objects.get_or_create(
name="StVal",
user_limit=2
)

fact_game, fact_created = Game.objects.get_or_create(
name=u'Факторизация',
choose_command=u"факторизация",
discussion=discussion,
description=u"В этой игре бот предложит рассмотреть ваши отношения как набор различных факторов и задаст вопросы про каждый из них по отдельности"
)
fact_game.save()

root_dir = os.path.dirname(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
categories = self.read_categories(os.path.join(root_dir, 'storage/fact_categories.csv'))
questions = self.read_questions(os.path.join(root_dir, 'storage/fact_questions.csv'))
for category_id, name in categories.items():
category, cat_created = QuestionCategory.objects.get_or_create(
name=name,
game=fact_game
)
for question, question_data in questions.items():
if question_data[1] == category.id:
question, question_created = Question.objects.get_or_create(
text=question_data[0]
)
category.questions.add(question)

self.stdout.write(self.style.SUCCESS(u'Successfully created game "%s"' % fact_game))
22 changes: 22 additions & 0 deletions data/management/commands/generate_promo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.core.management.base import BaseCommand, CommandError
from data.models import PromoCode, Discussion

import csv

class Command(BaseCommand):
help = 'Creates StVal Discussion + Promos'

def handle(self, *args, **options):
stValDiscussion, disc_created = Discussion.objects.get_or_create(
name="StVal",
user_limit=2
)
with open('storage/promo_codes.csv', 'r') as f:
reader = csv.reader(f)

for promo_code in reader:
promo, created = PromoCode.objects.get_or_create(
discussion=stValDiscussion,
code=promo_code[1]
)
self.stdout.write(self.style.SUCCESS('Successfully created promo_code "%s"' % promo_code[1]))
120 changes: 120 additions & 0 deletions data/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import ReplyKeyboardMarkup, KeyboardButton
from django.db import models
from django.contrib.postgres.fields import JSONField

from const import *


class User(models.Model):
telegram_id = models.IntegerField()
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
username = models.CharField(max_length=255)


class Discussion(models.Model):
name = models.CharField(max_length=255)
user_limit = models.IntegerField()
members = models.ManyToManyField(User, through='DiscussionMembership')


class PromoCode(models.Model):
code = models.CharField(max_length=255)
discussion = models.ForeignKey(Discussion, on_delete=models.CASCADE)


BANNED_FACTORS_LEN = 4
CHOSEN_FACTORS_LEN = 4

START_USER_STATE = {
'category': 0,
'question': 0
}


class Game(models.Model):
name = models.CharField(max_length=255)
choose_command = models.CharField(max_length=255)
discussion = models.ForeignKey(Discussion, on_delete=models.CASCADE)
description = models.TextField(blank=True)

def process_state(self, state, msg_text):
fact_name = u'Факторизация'
if self.name == fact_name:
return self.process_fact_state(state, msg_text)
else:
send_text = GAME_OVER_MSG
keyboard = [[InlineKeyboardButton(NEXT_QUESTION_CMD)]]
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)
return state, send_text, reply_markup

def process_fact_state(self, state, msg_text):
msg_text = msg_text.replace('/', '')
# TODO reorder game cats
game_categories = self.questioncategory_set.all()

if state.get('category'):
matched_categories = game_categories.filter(id=state.get('category'))
else:
matched_categories = game_categories.filter(name__contains=msg_text)

if state.get('category') or matched_categories:

matched_category = matched_categories[0]
state['category'] = matched_category.id

cat_questions = list(matched_category.questions.all())

if state.get('question') >= len(cat_questions):
send_text = NO_MORE_QUESTIONS_MSG
state = START_USER_STATE.copy()

keyboard = [[InlineKeyboardButton(BACK_CMD)]]
else:
current_question = cat_questions[state.get('question')]
send_text = current_question.text

state['question'] += 1

keyboard = [[InlineKeyboardButton(NEXT_QUESTION_CMD)],
[InlineKeyboardButton(BACK_CMD)]]
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)
else:
send_text = SELECT_CATEGORY_MSG

fact_cats = [
[KeyboardButton("{}".format(cat.name))] for cat in reversed(game_categories)
]
fact_cats.append([KeyboardButton(FINISH_FACT_CMD)])
reply_markup = ReplyKeyboardMarkup(fact_cats, one_time_keyboard=True)
state['category'] = 0

return state, send_text, reply_markup

def __unicode__(self):
return self.name


class Question(models.Model):
text = models.TextField(blank=True)

class Meta:
ordering = ['id']


class QuestionCategory(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
questions = models.ManyToManyField(Question)


class DiscussionMembership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
discussion = models.ForeignKey(Discussion, on_delete=models.CASCADE)
state = JSONField(null=True)
promo_code = models.CharField(max_length=255)
active_game = models.ForeignKey(Game, null=True, on_delete=models.SET_NULL)
ready = models.BooleanField(default=False)
category = models.CharField(max_length=255,null=True)
16 changes: 16 additions & 0 deletions data/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
WSGI config for test_projecrt project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_projecrt.settings")

application = get_wsgi_application()
Loading

0 comments on commit f6f9e7f

Please sign in to comment.