<commit_before># coding=UTF8
__author__ = 'daedaluschan'

# importing libraries

import sys, time
import telepot
from telepot.delegate import per_chat_id, create_open

from datetime import date
from types import *
from enum import Enum
import uuid

from types import  *

# Constants & global data

allPoll = {}
botName = 'HKGathering_bot'
start_private_url = 'https://telegram.me/' + botName + '?start='
start_group_url = 'https://telegram.me/' + botName + '?startgroup='

# Defining enums

class ConverType(Enum):
    nothing = 1
    create_poll = 2
    response_poll = 3

class CreatePollFlow(Enum):
    not_start = 1
    poll_question = 2
    poll_choice = 3

# Response data structure

class Response():
    def __init__(self):
        self.userid = 0
        self.display_name = ''
        self.preference = []

    def __str__(self):
        return '[response_obj][' + self.userid.__str__() + '][' + self.display_name + '][' + \
               len(self.preference).__str__() + ']'

    @property
    def userid(self):
        return self._userid

    @userid.setter
    def userid(self, value):
        self._userid = value

    @property
    def preference(self):
        return self._preference

    @preference.setter
    def preference(self, value):
        self._preference = value

    @property
    def display_name(self):
        return self._display_name

    @display_name.setter
    def display_name(self, value):
        self._display_name = value

# Poll data structure

class Poll():
    def __init__(self):
        self.question = ''
        self.creatorId = 0
        self.choices = []
        self.groupId = 0
        self.response = {}

    def __str__(self):
        choice_str = ''
        response_str = ''
        for each_choice in self.choices:
            choice_str = choice_str + ' - ' + each_choice
        for each_response in self.response:
            response_str = response_str + '<' + self.response[each_response].__str__() + '>'
        return '[Poll_obj][question: ' + self.question + '][choice: ' + choice_str + '][group: ' + \
               self.groupId.__str__() + '][all_resp: ' + response_str + ']'

    @property
    def question(self):
        return self._question

    @question.setter
    def question(self, value):
        self._question = value

    @property
    def creatorId(self):
        return self._creatorId

    @creatorId.setter
    def creatorId(self, value):
        self._creatorId = value

    @property
    def choices(self):
        return self._choices

    @choices.setter
    def choices(self, value):
        self._choices = value

    @property
    def groupId(self):
        return self._groupId

    @groupId.setter
    def groupId(self, value):
        self._groupId = value

    @property
    def survey_str(self, response_attached=[]):
        choice_str = ''
        i = 1
        for choice in self.choices:
            if len(response_attached) == 0:
                choice_str = choice_str + '- ' + choice.encode('utf-8') + '\n'
            else:
                choice_str = choice_str + '- /' + i.__str__() + ' ' + choice.encode('utf-8') + \
                ' -- 現在回應：' + 'Yes' if response_attached[i-1] else 'No' +' \n'
            i += 1

        print('after choice str is being constructed')
        survey_text = '依家問你：' + self.question.encode('utf-8') + '\n\n現有選擇係：\n\n' + choice_str + '\n'

        return survey_text

    @property
    def response(self):
        return self._response

    @response.setter
    def response(self, value):
        self._response = value

    def genNullResponse(self):
        preference_vector = []
        for each_choice in self.choices:
            preference_vector.append(False)
        return preference_vector

# main class of message handling

class HKGathering(telepot.helper.ChatHandler):
    def __init__(self, seed_tuple, timeout):
        super(HKGathering, self).__init__(seed_tuple, timeout)
        print('constructor is being called')
        self._converType = ConverType.nothing
        self._createPollFlow = CreatePollFlow.not_start
        self.__uid = ''
        self._poll = Poll()

    def complte_poll_creation(self):
        self.__uid = uuid.uuid4().hex
        allPoll[self.__uid] = self._poll
        self.sender.sendMessage(text='用呢條 link 將問題放落 chat group 裡邊：\n' +
                                     start_group_url + self.__uid)
        self._converType = ConverType.nothing

    def get_invited(self, poll_id):
        self._poll = allPoll[poll_id]
        print('Linked poll: ' + self._poll.__str__())
        choice_str = ''
        for choice in self._poll.choices:
            choice_str = choice_str + choice.encode('utf-8') + '\n'
        ans_link = '/answer' +'_' + poll_id.encode(encoding='utf-8')
        show_keyboard = {'keyboard': [['開始回應']]}

        self.sender.sendMessage(text=self._poll.survey_str + '\n' +
                                     '請用 ' + ans_link +  ' 回應問題。\n' +
                                     '或者用 pop up 鍵盤開始回應。',
                                reply_markup=show_keyboard)

    def initiate_survey(self, poll_id, target_id, display_name):
        print('answer to: ' + target_id.__str__())
        new_response = Response()
        new_response.userid = target_id
        new_response.display_name = display_name
        new_response.preference = allPoll[poll_id].genNullResponse()
        allPoll[poll_id].response[target_id.__str__()] = new_response
        # allPoll[poll_id].response.append({target_id.__str__(): new_response})

        print ('new_response: ' + new_response.__str__())
        print ('saved resposne: ' + allPoll[poll_id].response[target_id.__str__()].__str__())

        show_keyboard = {'keyboard': [['開始']]}
        self.bot.sendMessage(target_id,
                             text=self._poll.survey_str + '\n' +
                                  '請用 /begin 或者用 pop up 鍵盤開始。',
                             reply_markup=show_keyboard)

    def start_survey(self, poll_id, userid):
        print('start survey with poll id: ' + poll_id)
        self._poll = allPoll[poll_id]
        self.sender.sendMessage(text=self._poll.survey_str(response_attached=allPoll[poll_id].response[userid.__str__()].preference) +
                                     '\n' + '或者用 /add_pref 加入新選項。')

    def on_message(self, msg):
        print('on_message() is being called')
        print('==== all poll in cache ====')
        if len(allPoll) == 0:
            print('No poll yet')
        else:
            for each_poll in allPoll:
                print(allPoll[each_poll].__str__())
        print('========== END ============')
        flavor = telepot.flavor(msg)

        print('flavor: ' + flavor)

        # normal message
        if flavor == 'normal':
            content_type, chat_type, _chat_id = telepot.glance2(msg)
            print('Normal Message:', content_type, chat_type, _chat_id, '; message content: ', msg)

            if content_type == 'text' and chat_type == 'private':

                if self._converType == ConverType.nothing:

                    if msg['text'] == '/start':
                        self.sender.sendMessage(text='今日的日期係：' + str(date.today()) + '. \n' +
                                                     '用 /new 黎 create 一個新問題，' +
                                                     '或者用 /help 睇其他選項。')
                    elif msg['text'] == '/new':
                        self._converType = ConverType.create_poll
                        self._createPollFlow = CreatePollFlow.poll_question
                        self._poll.creatorId = msg['from']['id']
                        self.sender.sendMessage(text='唔該 send 你個問題俾我。')

                    elif msg['text'] == '/help':
                        self.sender.sendMessage(text='用 /new 黎 create 一個新問題，\n' +
                                                     '或者用 /result 黎查詢回應統計。\n')

                    elif msg['text'].encode(encoding='utf-8') == '開始' or msg['text'].startswith('/begin'):
                        found_poll = 0
                        for poll_key in allPoll.keys():
                            print ('in_all_poll: ' + allPoll[poll_key].__str__())
                            for each_resp in allPoll[poll_key].response:
                                print('each_resp: ' + allPoll[poll_key].response[each_resp].__str__())
                                if allPoll[poll_key].response[each_resp].userid == msg['from']['id']:
                                    found_poll = poll_key
                        if found_poll != 0:
                            self._converType = ConverType.response_poll
                            self.start_survey(found_poll, msg['from']['id'])
                        else:
                            self.sender.sendMessage(text='搵唔到你的問題。')
                    else:
                        self.sender.sendMessage(text='唔知你想點，麻煩你再試過。\n' +
                                                     '或者用 /help 睇其他選項。')
                elif self._converType == ConverType.create_poll:
                    if msg['text'] == '/done':
                        self.complte_poll_creation()
                    elif msg['text'] != '':
                        if self._createPollFlow == CreatePollFlow.poll_question:
                            self._poll.question = msg['text']
                            self._createPollFlow = CreatePollFlow.poll_choice
                            self.sender.sendMessage(text='好，咁你自己有乜意見？')
                        elif self._createPollFlow == CreatePollFlow.poll_choice:
                            self._poll.choices.append(msg['text'])
                            self.sender.sendMessage(text='好，仲有冇？有就繼續 send 下個個選擇。\n' +
                                                         '如果冇就用 /done 完成建立問題。')

            elif content_type == 'text' and chat_type == 'group':
                if msg['text'].startswith('/start@' + botName):
                    print('invited into group')
                    poll_id = msg['text'].split(' ')[1]
                    self.get_invited(poll_id)
                    allPoll[poll_id].groupId = _chat_id
                elif msg['text'].startswith('/answer_'):
                    poll_id = msg['text'].split('_')[1].split('@')[0]
                    self.initiate_survey(poll_id,
                                         target_id=msg['from']['id'],
                                         display_name=msg['from']['first_name'] + ' ' + msg['from']['last_name'])
                elif msg['text'].encode(encoding='utf-8') == '開始回應':
                    orig_text = msg['reply_to_message']['text']
                    poll_id = orig_text.split('/answer_')[1].split(' ')[0]
                    self.initiate_survey(poll_id,
                                         target_id=msg['from']['id'],
                                         display_name=msg['from']['first_name'] + ' ' + msg['from']['last_name'])

            print('Poll:' + self._poll.__str__())
        else:
            raise telepot.BadFlavor(msg)

TOKEN = sys.argv[1]  # get token from command-line

bot = telepot.DelegatorBot(TOKEN, [
    (per_chat_id(), create_open(HKGathering, timeout=20)), ])
print('Listening ...')
bot.notifyOnMessage(run_forever=True)
<commit_msg>added debug msg<commit_after># coding=UTF8
__author__ = 'daedaluschan'

# importing libraries

import sys, time
import telepot
from telepot.delegate import per_chat_id, create_open

from datetime import date
from types import *
from enum import Enum
import uuid

from types import  *

# Constants & global data

allPoll = {}
botName = 'HKGathering_bot'
start_private_url = 'https://telegram.me/' + botName + '?start='
start_group_url = 'https://telegram.me/' + botName + '?startgroup='

# Defining enums

class ConverType(Enum):
    nothing = 1
    create_poll = 2
    response_poll = 3

class CreatePollFlow(Enum):
    not_start = 1
    poll_question = 2
    poll_choice = 3

# Response data structure

class Response():
    def __init__(self):
        self.userid = 0
        self.display_name = ''
        self.preference = []

    def __str__(self):
        return '[response_obj][' + self.userid.__str__() + '][' + self.display_name + '][' + \
               len(self.preference).__str__() + ']'

    @property
    def userid(self):
        return self._userid

    @userid.setter
    def userid(self, value):
        self._userid = value

    @property
    def preference(self):
        return self._preference

    @preference.setter
    def preference(self, value):
        self._preference = value

    @property
    def display_name(self):
        return self._display_name

    @display_name.setter
    def display_name(self, value):
        self._display_name = value

# Poll data structure

class Poll():
    def __init__(self):
        self.question = ''
        self.creatorId = 0
        self.choices = []
        self.groupId = 0
        self.response = {}

    def __str__(self):
        choice_str = ''
        response_str = ''
        for each_choice in self.choices:
            choice_str = choice_str + ' - ' + each_choice
        for each_response in self.response:
            response_str = response_str + '<' + self.response[each_response].__str__() + '>'
        return '[Poll_obj][question: ' + self.question + '][choice: ' + choice_str + '][group: ' + \
               self.groupId.__str__() + '][all_resp: ' + response_str + ']'

    @property
    def question(self):
        return self._question

    @question.setter
    def question(self, value):
        self._question = value

    @property
    def creatorId(self):
        return self._creatorId

    @creatorId.setter
    def creatorId(self, value):
        self._creatorId = value

    @property
    def choices(self):
        return self._choices

    @choices.setter
    def choices(self, value):
        self._choices = value

    @property
    def groupId(self):
        return self._groupId

    @groupId.setter
    def groupId(self, value):
        self._groupId = value

    def gen_survey_str(self, response_attached=[]):
        choice_str = ''
        i = 1
        for choice in self.choices:
            if len(response_attached) == 0:
                choice_str = choice_str + '- ' + choice.encode('utf-8') + '\n'
            else:
                choice_str = choice_str + '- /' + i.__str__() + ' ' + choice.encode('utf-8') + \
                ' -- 現在回應：' + 'Yes' if response_attached[i-1] else 'No' +' \n'
            i += 1

        survey_text = '依家問你：' + self.question.encode('utf-8') + '\n\n現有選擇係：\n\n' + choice_str + '\n'

        return survey_text

    @property
    def response(self):
        return self._response

    @response.setter
    def response(self, value):
        self._response = value

    def genNullResponse(self):
        preference_vector = []
        for each_choice in self.choices:
            preference_vector.append(False)
        return preference_vector

# main class of message handling

class HKGathering(telepot.helper.ChatHandler):
    def __init__(self, seed_tuple, timeout):
        super(HKGathering, self).__init__(seed_tuple, timeout)
        print('constructor is being called')
        self._converType = ConverType.nothing
        self._createPollFlow = CreatePollFlow.not_start
        self.__uid = ''
        self._poll = Poll()

    def complte_poll_creation(self):
        self.__uid = uuid.uuid4().hex
        allPoll[self.__uid] = self._poll
        self.sender.sendMessage(text='用呢條 link 將問題放落 chat group 裡邊：\n' +
                                     start_group_url + self.__uid)
        self._converType = ConverType.nothing

    def get_invited(self, poll_id):
        self._poll = allPoll[poll_id]
        print('Linked poll: ' + self._poll.__str__())
        choice_str = ''
        for choice in self._poll.choices:
            choice_str = choice_str + choice.encode('utf-8') + '\n'
        ans_link = '/answer' +'_' + poll_id.encode(encoding='utf-8')
        show_keyboard = {'keyboard': [['開始回應']]}

        self.sender.sendMessage(text=self._poll.survey_str + '\n' +
                                     '請用 ' + ans_link +  ' 回應問題。\n' +
                                     '或者用 pop up 鍵盤開始回應。',
                                reply_markup=show_keyboard)

    def initiate_survey(self, poll_id, target_id, display_name):
        print('answer to: ' + target_id.__str__())
        new_response = Response()
        new_response.userid = target_id
        new_response.display_name = display_name
        new_response.preference = allPoll[poll_id].genNullResponse()
        allPoll[poll_id].response[target_id.__str__()] = new_response
        # allPoll[poll_id].response.append({target_id.__str__(): new_response})

        print ('new_response: ' + new_response.__str__())
        print ('saved resposne: ' + allPoll[poll_id].response[target_id.__str__()].__str__())

        show_keyboard = {'keyboard': [['開始']]}
        self.bot.sendMessage(target_id,
                             text=self._poll.gen_survey_str() + '\n' +
                                  '請用 /begin 或者用 pop up 鍵盤開始。',
                             reply_markup=show_keyboard)

    def start_survey(self, poll_id, userid):
        print('start survey with poll id: ' + poll_id)
        self._poll = allPoll[poll_id]
        self.sender.sendMessage(text=self._poll.gen_survey_str(response_attached=allPoll[poll_id].response[userid.__str__()].preference) +
                                     '\n或者用 /add_pref 加入新選項。')

    def on_message(self, msg):
        print('on_message() is being called')
        print('==== all poll in cache ====')
        if len(allPoll) == 0:
            print('No poll yet')
        else:
            for each_poll in allPoll:
                print(allPoll[each_poll].__str__())
        print('========== END ============')
        flavor = telepot.flavor(msg)

        print('flavor: ' + flavor)

        # normal message
        if flavor == 'normal':
            content_type, chat_type, _chat_id = telepot.glance2(msg)
            print('Normal Message:', content_type, chat_type, _chat_id, '; message content: ', msg)

            if content_type == 'text' and chat_type == 'private':

                if self._converType == ConverType.nothing:

                    if msg['text'] == '/start':
                        self.sender.sendMessage(text='今日的日期係：' + str(date.today()) + '. \n' +
                                                     '用 /new 黎 create 一個新問題，' +
                                                     '或者用 /help 睇其他選項。')
                    elif msg['text'] == '/new':
                        self._converType = ConverType.create_poll
                        self._createPollFlow = CreatePollFlow.poll_question
                        self._poll.creatorId = msg['from']['id']
                        self.sender.sendMessage(text='唔該 send 你個問題俾我。')

                    elif msg['text'] == '/help':
                        self.sender.sendMessage(text='用 /new 黎 create 一個新問題，\n' +
                                                     '或者用 /result 黎查詢回應統計。\n')

                    elif msg['text'].encode(encoding='utf-8') == '開始' or msg['text'].startswith('/begin'):
                        found_poll = 0
                        for poll_key in allPoll.keys():
                            print ('in_all_poll: ' + allPoll[poll_key].__str__())
                            for each_resp in allPoll[poll_key].response:
                                print('each_resp: ' + allPoll[poll_key].response[each_resp].__str__())
                                if allPoll[poll_key].response[each_resp].userid == msg['from']['id']:
                                    found_poll = poll_key
                        if found_poll != 0:
                            self._converType = ConverType.response_poll
                            self.start_survey(found_poll, msg['from']['id'])
                        else:
                            self.sender.sendMessage(text='搵唔到你的問題。')
                    else:
                        self.sender.sendMessage(text='唔知你想點，麻煩你再試過。\n' +
                                                     '或者用 /help 睇其他選項。')
                elif self._converType == ConverType.create_poll:
                    if msg['text'] == '/done':
                        self.complte_poll_creation()
                    elif msg['text'] != '':
                        if self._createPollFlow == CreatePollFlow.poll_question:
                            self._poll.question = msg['text']
                            self._createPollFlow = CreatePollFlow.poll_choice
                            self.sender.sendMessage(text='好，咁你自己有乜意見？')
                        elif self._createPollFlow == CreatePollFlow.poll_choice:
                            self._poll.choices.append(msg['text'])
                            self.sender.sendMessage(text='好，仲有冇？有就繼續 send 下個個選擇。\n' +
                                                         '如果冇就用 /done 完成建立問題。')

            elif content_type == 'text' and chat_type == 'group':
                if msg['text'].startswith('/start@' + botName):
                    print('invited into group')
                    poll_id = msg['text'].split(' ')[1]
                    self.get_invited(poll_id)
                    allPoll[poll_id].groupId = _chat_id
                elif msg['text'].startswith('/answer_'):
                    poll_id = msg['text'].split('_')[1].split('@')[0]
                    self.initiate_survey(poll_id,
                                         target_id=msg['from']['id'],
                                         display_name=msg['from']['first_name'] + ' ' + msg['from']['last_name'])
                elif msg['text'].encode(encoding='utf-8') == '開始回應':
                    orig_text = msg['reply_to_message']['text']
                    poll_id = orig_text.split('/answer_')[1].split(' ')[0]
                    self.initiate_survey(poll_id,
                                         target_id=msg['from']['id'],
                                         display_name=msg['from']['first_name'] + ' ' + msg['from']['last_name'])

            print('Poll:' + self._poll.__str__())
        else:
            raise telepot.BadFlavor(msg)

TOKEN = sys.argv[1]  # get token from command-line

bot = telepot.DelegatorBot(TOKEN, [
    (per_chat_id(), create_open(HKGathering, timeout=20)), ])
print('Listening ...')
bot.notifyOnMessage(run_forever=True)
<|endoftext|>


<commit_before>__author__ = 'daedaluschan'

# importing libraries

import sys, time
import telepot
from telepot.delegate import per_chat_id, create_open

from datetime import date
from types import *
from enum import Enum

# Constants

start_group_url = 'https://telegram.me/HKGathering_bot?startgroup=test'

# Defining enums

class ConverType(Enum):
    nothing=1
    create_poll=2

class CreatePollFlow(Enum):
    not_start=1
    poll_question=2
    poll_choice=3

# Poll data structure

class Poll():
    def __init__(self):
        self._question = ''
        self._choices = []

    def __str__(self):
        choice_str = ''
        for each_choice in self._choices:
            choice_str = choice_str + ' - ' + each_choice
        return  'question: ' + self._question + '; choice: ' + choice_str

# main class of message handling

class HKGathering(telepot.helper.ChatHandler):
    def __init__(self, seed_tuple, timeout):
        super(HKGathering, self).__init__(seed_tuple, timeout)
        print('constructor is being called')
        self._converType = ConverType.nothing
        self._createPollFlow = CreatePollFlow.not_start
        self._poll = Poll()

    def on_message(self, msg):
        print('on_message() is being called')
        flavor = telepot.flavor(msg)

        # normal message
        if flavor == 'normal':
            content_type, chat_type, _chat_id = telepot.glance2(msg)
            print('Normal Message:', content_type, chat_type, _chat_id, '; message content: ', msg)

            if self._converType == ConverType.nothing:

                if msg['text'] == '/start':
                    self.sender.sendMessage(text='今日的日期係：' + str(date.today()) + '. \n' +
                                                 'User /new to create new poll. Or,' +
                                                 'use /help for more options')
                elif msg['text'] == '/new':
                    self._converType = ConverType.create_poll
                    self._createPollFlow = CreatePollFlow.poll_question
                    self.sender.sendMessage(text='Please send me over the question you want to ask.')

                elif msg['text'] == '/help':
                    self.sender.sendMessage(text='/new - create new poll. \n' +
                                                 '/result - for query on the latest result \n')
                else:
                    self.sender.sendMessage(text='I don\'t understand what you are saying !\n' +
                                                 'Try again ! Or use /help for assistance.')
            elif self._converType == ConverType.create_poll :
                if msg['text'] == '/done':
                    self.sender.sendMessage(text='Thank You!')
                elif msg['text'] != '':
                    if self._createPollFlow == CreatePollFlow.poll_question:
                        self._poll._question = msg['text']
                        self._createPollFlow = CreatePollFlow.poll_choice
                        self.sender.sendMessage(text='Please give me your preference')
                    elif self._createPollFlow == CreatePollFlow.poll_choice:
                        self._poll._choices.append(msg['text'])
                        self.sender.sendMessage(text='Please give me your next preference, if any.\n' +
                                                     'use /done to complete creating your poll.')

        else:
            raise telepot.BadFlavor(msg)

TOKEN = sys.argv[1]  # get token from command-line

bot = telepot.DelegatorBot(TOKEN, [
    (per_chat_id(), create_open(HKGathering, timeout=20)),])
print('Listening ...')
bot.notifyOnMessage(run_forever=True)
<commit_msg>coding=utf8<commit_after># coding=UTF8
__author__ = 'daedaluschan'

# importing libraries

import sys, time
import telepot
from telepot.delegate import per_chat_id, create_open

from datetime import date
from types import *
from enum import Enum

# Constants

start_group_url = 'https://telegram.me/HKGathering_bot?startgroup=test'

# Defining enums

class ConverType(Enum):
    nothing=1
    create_poll=2

class CreatePollFlow(Enum):
    not_start=1
    poll_question=2
    poll_choice=3

# Poll data structure

class Poll():
    def __init__(self):
        self._question = ''
        self._choices = []

    def __str__(self):
        choice_str = ''
        for each_choice in self._choices:
            choice_str = choice_str + ' - ' + each_choice
        return  'question: ' + self._question + '; choice: ' + choice_str

# main class of message handling

class HKGathering(telepot.helper.ChatHandler):
    def __init__(self, seed_tuple, timeout):
        super(HKGathering, self).__init__(seed_tuple, timeout)
        print('constructor is being called')
        self._converType = ConverType.nothing
        self._createPollFlow = CreatePollFlow.not_start
        self._poll = Poll()

    def on_message(self, msg):
        print('on_message() is being called')
        flavor = telepot.flavor(msg)

        # normal message
        if flavor == 'normal':
            content_type, chat_type, _chat_id = telepot.glance2(msg)
            print('Normal Message:', content_type, chat_type, _chat_id, '; message content: ', msg)

            if self._converType == ConverType.nothing:

                if msg['text'] == '/start':
                    self.sender.sendMessage(text='今日的日期係：' + str(date.today()) + '. \n' +
                                                 'User /new to create new poll. Or,' +
                                                 'use /help for more options')
                elif msg['text'] == '/new':
                    self._converType = ConverType.create_poll
                    self._createPollFlow = CreatePollFlow.poll_question
                    self.sender.sendMessage(text='Please send me over the question you want to ask.')

                elif msg['text'] == '/help':
                    self.sender.sendMessage(text='/new - create new poll. \n' +
                                                 '/result - for query on the latest result \n')
                else:
                    self.sender.sendMessage(text='I don\'t understand what you are saying !\n' +
                                                 'Try again ! Or use /help for assistance.')
            elif self._converType == ConverType.create_poll :
                if msg['text'] == '/done':
                    self.sender.sendMessage(text='Thank You!')
                elif msg['text'] != '':
                    if self._createPollFlow == CreatePollFlow.poll_question:
                        self._poll._question = msg['text']
                        self._createPollFlow = CreatePollFlow.poll_choice
                        self.sender.sendMessage(text='Please give me your preference')
                    elif self._createPollFlow == CreatePollFlow.poll_choice:
                        self._poll._choices.append(msg['text'])
                        self.sender.sendMessage(text='Please give me your next preference, if any.\n' +
                                                     'use /done to complete creating your poll.')

        else:
            raise telepot.BadFlavor(msg)

TOKEN = sys.argv[1]  # get token from command-line

bot = telepot.DelegatorBot(TOKEN, [
    (per_chat_id(), create_open(HKGathering, timeout=20)),])
print('Listening ...')
bot.notifyOnMessage(run_forever=True)
<|endoftext|>


<commit_before>from django.conf import settings
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.http import HttpResponse
from django.conf.urls.static import static
from django.contrib.staticfiles import views as static_views
from django.views.defaults import permission_denied

from .api import UserView, GetJWTView
from users.views import LoginView

def show_login(request):
    html = ""
    if request.user.is_authenticated:
        html += "%s" % request.user
    else:
        html += "not logged in"
    html += ""
    return HttpResponse(html)

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^saml2/', include('djangosaml2.urls')),
    url(r'^accounts/profile/', show_login),
    url(r'^accounts/', include('allauth.urls')),
    url(r'^oauth2/applications/', permission_denied),
    url(r'^oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
    url(r'^user/(?P<username>[\w.@+-]+)/?$', UserView.as_view()),
    url(r'^user/$', UserView.as_view()),
    url(r'^jwt-token/$', GetJWTView.as_view()),
    url(r'^login/$', LoginView.as_view()),
)

if settings.DEBUG:
    urlpatterns += [url(r'^static/(?P<path>.*)$', static_views.serve)]
<commit_msg>Add logout method<commit_after>from django.conf import settings
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.http import HttpResponse
from django.conf.urls.static import static
from django.contrib.staticfiles import views as static_views
from django.views.defaults import permission_denied
from allauth.account.views import LogoutView
from .api import UserView, GetJWTView
from users.views import LoginView

def show_login(request):
    html = ""
    if request.user.is_authenticated:
        html += "%s" % request.user
    else:
        html += "not logged in"
    html += ""
    return HttpResponse(html)

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^saml2/', include('djangosaml2.urls')),
    url(r'^accounts/profile/', show_login),
    url(r'^accounts/', include('allauth.urls')),
    url(r'^oauth2/applications/', permission_denied),
    url(r'^oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
    url(r'^user/(?P<username>[\w.@+-]+)/?$', UserView.as_view()),
    url(r'^user/$', UserView.as_view()),
    url(r'^jwt-token/$', GetJWTView.as_view()),
    url(r'^login/$', LoginView.as_view()),
    url(r'^logout/$', LogoutView.as_view())
)

if settings.DEBUG:
    urlpatterns += [url(r'^static/(?P<path>.*)$', static_views.serve)]
<|endoftext|>


<commit_before>import eventlet
from errors import ExpectedException
try:
    import json
except ImportError:
    import simplejson as json
import datetime

def get_now():
  return datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')

class User(object):
    _options = {
        'reflective': True,
        'moderated_message': True,
        'per_connection_subscriptions': False,
    }

    def __init__(self, server, name, **options):
        self.server = server
        self.name = name
        self.connections = []
        self.channels = {}
        self._temp_cookie = ""
        self.update_options(**self._options)
        self.update_options(**options)

    def serialize(self):
        return {
            'channels': [ chan.name for chan in self.channels ],
            'connections': [ conn.id for conn in self.connections ],
            'name': self.name,
            'options': dict([ (key, getattr(self, key)) for key in self._options])
        }

    def extract_valid_options(self, options):
        return dict([ (key, options.pop(key, self._options[key])) for key in self._options ])

    def update_options(self, **options):
        # TODO: this can't remain so generic forever. At some point we need
        #       better checks on values, such as the list of dictionaries
        #       for history, or the polling options.
        # TODO: add support for lists (we only have dicts now)
        # TODO: Probably should make this whole function recursive... though
        #       we only really have one level of nesting now.
        # TODO: most of this function is duplicated from Channel#update_options
        #       (including the TODOs above), could be a lot DRYer
        for key, val in options.items():
            if key not in self._options:
                raise ValueError("Invalid keyword argument %s" % (key))
            default = self._options[key]
            cls = default.__class__
            if cls in (unicode, str):
                cls = basestring
            if not isinstance(val, cls):
                raise ValueError("Invalid type for %s (should be %s)" % (key, default.__class__))
            if key == 'state':
                self.state_replace(val)
                continue
            if isinstance(val, dict):
                for _key, _val in val.items():
                    if _key not in self._options[key]:
                        raise ValueError("Invalid keyword argument %s" % (_key))
                    default = self._options[key][_key]
                    cls = default.__class__
                    if isinstance(default, float) and isinstance(_val, int):
                        _val = float(_val)
                    if cls in (unicode, str):
                        cls = basestring
                    if not isinstance(_val, cls):
                        raise ValueError("%s is Invalid type for %s (should be %s)" % (_val, _key, default.__class__))
        # two loops forces exception *before* any of the options are set.
        for key, val in options.items():
            # this should create copies of any dicts or lists that are options
            if isinstance(val, dict) and hasattr(self, key):
                getattr(self, key).update(val)
            else:
                setattr(self, key, val.__class__(val))

    def add_connection(self, conn):
        self.connections.append(conn)
        conn.user = self
        # call later...
        eventlet.spawn(self._send_initial_subscriptions, conn)

    def _send_initial_subscriptions(self, conn):
        for channel in self.channels:
            frame = channel._build_subscribe_frame(self)
            conn.send_frame('SUBSCRIBE', frame)

    def remove_connection(self, conn):
        self.connections.remove(conn)

        # Remove the connection from the channels it was subscribed to,
        # unsubscribing the user from any channels which they no longer
        # have open connections to
        for (channel, channel_connections) in self.channels.items():
            if conn not in channel_connections:
                continue
            self.channels[channel].remove(conn)
            if not self.channels[channel] and self.per_connection_subscriptions:
                channel.unsubscribe(self, needs_auth=True, force_auth=True)

        if not self.connections:
            for (channel, connections) in self.channels.items():
                channel.unsubscribe(self, needs_auth=True, force_auth=True)
            # so the disconnect callback has a cookie
            self._temp_cookie = conn.get_cookie()
            self.server.remove_user(self.name)

    def channel_subscribed(self, channel, conn=None):
        if channel not in self.channels:
            self.channels[channel] = [ conn ]
        elif conn not in self.channels[channel]:
            self.channels[channel].append(conn)

    def channel_unsubscribed(self, channel):
        if channel in self.channels:
            del self.channels[channel]

    def get_name(self):
        return self.name

    def send_frame(self, name, args={}, omit=None, channel=None):
        if not self.per_connection_subscriptions:
            channel = None
        if channel and channel not in self.channels:
            return
        for conn in (self.channels[channel] if channel else self.connections):
            if conn is not omit:
                conn.send_frame(name, args)

    def get_cookie(self, conn=None):
        if conn:
            return conn.get_cookie()

        return self._temp_cookie or ""

    def send_message(self, recipient_name, payload, conn=None, needs_auth=True):
        try:
            encoded_payload = json.loads(payload)
        except:
            raise ExpectedException("Invalid json for payload")
        payload = encoded_payload
        if needs_auth and self.moderated_message:
            form = { 'sender': self.get_name(), 'recipient': recipient_name, 'recipient_exists': self.server.exists_user(recipient_name), 'payload': json.dumps(payload) }
            success, options = self.server.http_request('message', self.get_cookie(conn), form, conn=conn)
            self.server.maybe_auto_subscribe(self, options, conn=conn)
            if not success:
                raise ExpectedException(options.get('error', 'Unauthorized'))
            payload = options.get('override_payload', payload)
            recipient_name = options.get('override_recipient_name', recipient_name)
        elif not self.server.exists_user(recipient_name):
            raise ExpectedException('Invalid user name')

        recipient = self.server.get_user(recipient_name) if self.server.exists_user(recipient_name) else None

        frame = {"sender": self.get_name(), "recipient": recipient.get_name() if recipient else "null", "payload": payload, "datetime": get_now()}
        if recipient:
            recipient.send_frame('MESSAGE', frame)
        if self.reflective and (not recipient or recipient.name != self.name):
            self.send_frame('MESSAGE', frame)

<commit_msg>Fix channel list change during iteration error<commit_after>import eventlet
from errors import ExpectedException
try:
    import json
except ImportError:
    import simplejson as json
import datetime

def get_now():
  return datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')

class User(object):
    _options = {
        'reflective': True,
        'moderated_message': True,
        'per_connection_subscriptions': False,
    }

    def __init__(self, server, name, **options):
        self.server = server
        self.name = name
        self.connections = []
        self.channels = {}
        self._temp_cookie = ""
        self.update_options(**self._options)
        self.update_options(**options)

    def serialize(self):
        return {
            'channels': [ chan.name for chan in self.channels ],
            'connections': [ conn.id for conn in self.connections ],
            'name': self.name,
            'options': dict([ (key, getattr(self, key)) for key in self._options])
        }

    def extract_valid_options(self, options):
        return dict([ (key, options.pop(key, self._options[key])) for key in self._options ])

    def update_options(self, **options):
        # TODO: this can't remain so generic forever. At some point we need
        #       better checks on values, such as the list of dictionaries
        #       for history, or the polling options.
        # TODO: add support for lists (we only have dicts now)
        # TODO: Probably should make this whole function recursive... though
        #       we only really have one level of nesting now.
        # TODO: most of this function is duplicated from Channel#update_options
        #       (including the TODOs above), could be a lot DRYer
        for key, val in options.items():
            if key not in self._options:
                raise ValueError("Invalid keyword argument %s" % (key))
            default = self._options[key]
            cls = default.__class__
            if cls in (unicode, str):
                cls = basestring
            if not isinstance(val, cls):
                raise ValueError("Invalid type for %s (should be %s)" % (key, default.__class__))
            if key == 'state':
                self.state_replace(val)
                continue
            if isinstance(val, dict):
                for _key, _val in val.items():
                    if _key not in self._options[key]:
                        raise ValueError("Invalid keyword argument %s" % (_key))
                    default = self._options[key][_key]
                    cls = default.__class__
                    if isinstance(default, float) and isinstance(_val, int):
                        _val = float(_val)
                    if cls in (unicode, str):
                        cls = basestring
                    if not isinstance(_val, cls):
                        raise ValueError("%s is Invalid type for %s (should be %s)" % (_val, _key, default.__class__))
        # two loops forces exception *before* any of the options are set.
        for key, val in options.items():
            # this should create copies of any dicts or lists that are options
            if isinstance(val, dict) and hasattr(self, key):
                getattr(self, key).update(val)
            else:
                setattr(self, key, val.__class__(val))

    def add_connection(self, conn):
        self.connections.append(conn)
        conn.user = self
        # call later...
        eventlet.spawn(self._send_initial_subscriptions, conn)

    def _send_initial_subscriptions(self, conn):
        for (channel, channel_connections) in self.channels.items():
            frame = channel._build_subscribe_frame(self)
            conn.send_frame('SUBSCRIBE', frame)

    def remove_connection(self, conn):
        self.connections.remove(conn)

        # Remove the connection from the channels it was subscribed to,
        # unsubscribing the user from any channels which they no longer
        # have open connections to
        for (channel, channel_connections) in self.channels.items():
            if conn not in channel_connections:
                continue
            self.channels[channel].remove(conn)
            if not self.channels[channel] and self.per_connection_subscriptions:
                channel.unsubscribe(self, needs_auth=True, force_auth=True)

        if not self.connections:
            for (channel, connections) in self.channels.items():
                channel.unsubscribe(self, needs_auth=True, force_auth=True)
            # so the disconnect callback has a cookie
            self._temp_cookie = conn.get_cookie()
            self.server.remove_user(self.name)

    def channel_subscribed(self, channel, conn=None):
        if channel not in self.channels:
            self.channels[channel] = [ conn ]
        elif conn not in self.channels[channel]:
            self.channels[channel].append(conn)

    def channel_unsubscribed(self, channel):
        if channel in self.channels:
            del self.channels[channel]

    def get_name(self):
        return self.name

    def send_frame(self, name, args={}, omit=None, channel=None):
        if not self.per_connection_subscriptions:
            channel = None
        if channel and channel not in self.channels:
            return
        for conn in (self.channels[channel] if channel else self.connections):
            if conn is not omit:
                conn.send_frame(name, args)

    def get_cookie(self, conn=None):
        if conn:
            return conn.get_cookie()

        return self._temp_cookie or ""

    def send_message(self, recipient_name, payload, conn=None, needs_auth=True):
        try:
            encoded_payload = json.loads(payload)
        except:
            raise ExpectedException("Invalid json for payload")
        payload = encoded_payload
        if needs_auth and self.moderated_message:
            form = { 'sender': self.get_name(), 'recipient': recipient_name, 'recipient_exists': self.server.exists_user(recipient_name), 'payload': json.dumps(payload) }
            success, options = self.server.http_request('message', self.get_cookie(conn), form, conn=conn)
            self.server.maybe_auto_subscribe(self, options, conn=conn)
            if not success:
                raise ExpectedException(options.get('error', 'Unauthorized'))
            payload = options.get('override_payload', payload)
            recipient_name = options.get('override_recipient_name', recipient_name)
        elif not self.server.exists_user(recipient_name):
            raise ExpectedException('Invalid user name')

        recipient = self.server.get_user(recipient_name) if self.server.exists_user(recipient_name) else None

        frame = {"sender": self.get_name(), "recipient": recipient.get_name() if recipient else "null", "payload": payload, "datetime": get_now()}
        if recipient:
            recipient.send_frame('MESSAGE', frame)
        if self.reflective and (not recipient or recipient.name != self.name):
            self.send_frame('MESSAGE', frame)

<|endoftext|>


<commit_before>import os
import logging
import requests
import json

class Zomat(object):
    """
    Fetch data using the Zomato API
    """

    def __init__(self):
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger("Zomat")
        self.userKey = os.environ.get("USER_KEY")
        self.headers = {"Accept":"application/json", "user-key": self.userKey}

    def getLocation(self, location):
        """
        Get Zomato entity_id and entity_type
        """
        entity_id = 0
        entity_type = str()
        self.logger.info("Looking up for location : %s", location)
        search_url = "https://developers.zomato.com/api/v2.1/locations?query="+location
        search_resp = requests.get(search_url,headers=self.headers)
        search_resp_dict = json.loads(search_resp.text)
        loc_sug_list = search_resp_dict['location_suggestions']
        for loc_sug in loc_sug_list:
            entity_type = loc_sug["entity_type"]
            entity_id = loc_sug["entity_id"]
            if entity_id and entity_type:
                self.logger.info("entity_id = %d, entity_type = %s", entity_id, entity_type)
        return entity_id, entity_type

    def getBestRestaurants(self, entity_id, entity_type, cuisine_id = 0):
        restaurant_list = []
        if cuisine_id == 0:
            self.logger.info("No specific cuisine")
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+str(entity_type)+"&count=5&sort=rating&order=desc"
        else:
            self.logger.info("Finding Restaurants as per cuisines")
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+str(entity_type)+"&count=5&radius=5000&cuisines="+str(cuisine_id)+"&sort=rating&order=desc"
        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        restaurants = (resp_dict['restaurants'])
        print ("Top 5 restaurants : ",restaurants)

        for i in restaurants:
            zomato_dict = {}
            zomato_dict['fbcard_name'] = i['restaurant']['name']
            zomato_dict['fbcard_subtitle'] = i['restaurant']['location']['address']
            zomato_dict['fbcard_url'] = i['restaurant']['url']
            zomato_dict['fbcard_photo'] = i['restaurant']['featured_image']
            zomato_dict['button_url'] = i['restaurant']['menu_url']
            zomato_dict['button_title'] = "Restaurant Menu"
            restaurant_list.append(zomato_dict)
        return restaurant_list

    def getCityID(self, city):
        """
        Get Zomato ID and other info for a City
        """
        city_id = 0
        zomato_url = "https://developers.zomato.com/api/v2.1/cities?q="+city
        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        # Assuming there is only one entry for this City
        city_id = resp_dict['location_suggestions'][0]['id']
        self.logger.info("For City : %s, got city_id = %d", city, city_id)
        return city_id

    def getCuisineID(self, city, cuisine):
        """
        Get the Zomate Cuisine ID
        """
        city_id = self.getCityID(city)
        if city_id != 0:
            zomato_url = "https://developers.zomato.com/api/v2.1/cuisines?city_id="+str(city_id)
            resp = requests.get(zomato_url,headers=self.headers)
            resp_dict = json.loads(resp.text)
            cusines = (resp_dict['cuisines'])
            for zcuisine in cusines:
                if cuisine.lower() == zcuisine['cuisine']['cuisine_name'].lower():
                    self.logger.info("For Cuisine : %s, cuisine_id = %d", cuisine, zcuisine['cuisine']['cuisine_id'])
                    return zcuisine['cuisine']['cuisine_id']

        # Cuisine not found
        self.logger.info("Cuisine, %s, not found for city %s", cuisine, city)
        return 0

    def getReviews(self, res_name, entity_id = 0, entity_type = ""):
        """
        Get the review for the specified Restaurant
        """
        self.logger.info("Restaurant review for : %s", res_name)
        res_review = []
        res_id = 0
        if entity_id == 0 and not entity_type:
            zomato_url = "https://developers.zomato.com/api/v2.1/search?q="+res_name
        else:
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+entity_type+"&q="+res_name

        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        restaurants = (resp_dict['restaurants'])
        print ("Found restaurants : ",restaurants)

        for r in restaurants:
            print (r['restaurant']['name'])
            # Sometimes the queries will contains results where the Restaurant
            # name is part of the address. So check specifically for the name
            if res_name == r['restaurant']['name']:
                zomato_dict = {}
                res_id = r['restaurant']['R']['res_id']
                self.logger.info("For %s, Restaurant ID = %d", res_name, res_id)
                zomato_dict['fbcard_name'] = r['restaurant']['name']
                zomato_dict['fbcard_subtitle'] = "Votes : " + str(r['restaurant']['user_rating']['votes']) + "\n" + "Average Cost for Two : " + str(r['restaurant']['average_cost_for_two'])
                zomato_dict['fbcard_url'] = r['restaurant']['url']
                zomato_dict['fbcard_photo'] = r['restaurant']['featured_image']
                menu_url = r['restaurant']['menu_url']
                menu_url.replace("menu", "reviews", 1)
                zomato_dict['button_url'] = menu_url
                zomato_dict['button_title'] = "Rating : " + r['restaurant']['user_rating']['aggregate_rating'] + " (" + r['restaurant']['user_rating']['rating_text'] + ")"
                res_review.append(zomato_dict)

        return res_review
<commit_msg>Review URL print<commit_after>import os
import logging
import requests
import json

class Zomat(object):
    """
    Fetch data using the Zomato API
    """

    def __init__(self):
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger("Zomat")
        self.userKey = os.environ.get("USER_KEY")
        self.headers = {"Accept":"application/json", "user-key": self.userKey}

    def getLocation(self, location):
        """
        Get Zomato entity_id and entity_type
        """
        entity_id = 0
        entity_type = str()
        self.logger.info("Looking up for location : %s", location)
        search_url = "https://developers.zomato.com/api/v2.1/locations?query="+location
        search_resp = requests.get(search_url,headers=self.headers)
        search_resp_dict = json.loads(search_resp.text)
        loc_sug_list = search_resp_dict['location_suggestions']
        for loc_sug in loc_sug_list:
            entity_type = loc_sug["entity_type"]
            entity_id = loc_sug["entity_id"]
            if entity_id and entity_type:
                self.logger.info("entity_id = %d, entity_type = %s", entity_id, entity_type)
        return entity_id, entity_type

    def getBestRestaurants(self, entity_id, entity_type, cuisine_id = 0):
        restaurant_list = []
        if cuisine_id == 0:
            self.logger.info("No specific cuisine")
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+str(entity_type)+"&count=5&sort=rating&order=desc"
        else:
            self.logger.info("Finding Restaurants as per cuisines")
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+str(entity_type)+"&count=5&radius=5000&cuisines="+str(cuisine_id)+"&sort=rating&order=desc"
        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        restaurants = (resp_dict['restaurants'])
        print ("Top 5 restaurants : ",restaurants)

        for i in restaurants:
            zomato_dict = {}
            zomato_dict['fbcard_name'] = i['restaurant']['name']
            zomato_dict['fbcard_subtitle'] = i['restaurant']['location']['address']
            zomato_dict['fbcard_url'] = i['restaurant']['url']
            zomato_dict['fbcard_photo'] = i['restaurant']['featured_image']
            zomato_dict['button_url'] = i['restaurant']['menu_url']
            zomato_dict['button_title'] = "Restaurant Menu"
            restaurant_list.append(zomato_dict)
        return restaurant_list

    def getCityID(self, city):
        """
        Get Zomato ID and other info for a City
        """
        city_id = 0
        zomato_url = "https://developers.zomato.com/api/v2.1/cities?q="+city
        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        # Assuming there is only one entry for this City
        city_id = resp_dict['location_suggestions'][0]['id']
        self.logger.info("For City : %s, got city_id = %d", city, city_id)
        return city_id

    def getCuisineID(self, city, cuisine):
        """
        Get the Zomate Cuisine ID
        """
        city_id = self.getCityID(city)
        if city_id != 0:
            zomato_url = "https://developers.zomato.com/api/v2.1/cuisines?city_id="+str(city_id)
            resp = requests.get(zomato_url,headers=self.headers)
            resp_dict = json.loads(resp.text)
            cusines = (resp_dict['cuisines'])
            for zcuisine in cusines:
                if cuisine.lower() == zcuisine['cuisine']['cuisine_name'].lower():
                    self.logger.info("For Cuisine : %s, cuisine_id = %d", cuisine, zcuisine['cuisine']['cuisine_id'])
                    return zcuisine['cuisine']['cuisine_id']

        # Cuisine not found
        self.logger.info("Cuisine, %s, not found for city %s", cuisine, city)
        return 0

    def getReviews(self, res_name, entity_id = 0, entity_type = ""):
        """
        Get the review for the specified Restaurant
        """
        self.logger.info("Restaurant review for : %s", res_name)
        res_review = []
        res_id = 0
        if entity_id == 0 and not entity_type:
            zomato_url = "https://developers.zomato.com/api/v2.1/search?q="+res_name
        else:
            zomato_url = "https://developers.zomato.com/api/v2.1/search?entity_id="+str(entity_id)+"&entity_type="+entity_type+"&q="+res_name

        resp = requests.get(zomato_url,headers=self.headers)
        resp_dict = json.loads(resp.text)
        restaurants = (resp_dict['restaurants'])
        print ("Found restaurants : ",restaurants)

        for r in restaurants:
            print (r['restaurant']['name'])
            # Sometimes the queries will contains results where the Restaurant
            # name is part of the address. So check specifically for the name
            if res_name == r['restaurant']['name']:
                zomato_dict = {}
                res_id = r['restaurant']['R']['res_id']
                self.logger.info("For %s, Restaurant ID = %d", res_name, res_id)
                zomato_dict['fbcard_name'] = r['restaurant']['name']
                zomato_dict['fbcard_subtitle'] = "Votes : " + str(r['restaurant']['user_rating']['votes']) + "\n" + "Average Cost for Two : " + str(r['restaurant']['average_cost_for_two'])
                zomato_dict['fbcard_url'] = r['restaurant']['url']
                zomato_dict['fbcard_photo'] = r['restaurant']['featured_image']
                menu_url = r['restaurant']['menu_url']
                review_url = menu_url.replace("menu", "reviews", 1)
                self.logger.info("Review URL = %s", review_url)
                zomato_dict['button_url'] = review_url
                zomato_dict['button_title'] = "Rating : " + r['restaurant']['user_rating']['aggregate_rating'] + " (" + r['restaurant']['user_rating']['rating_text'] + ")"
                res_review.append(zomato_dict)

        return res_review
<|endoftext|>


<commit_before>"""
The MIT License (MIT)

Copyright (c) 2015-present Rapptz

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from typing import Any, Dict, Optional, TYPE_CHECKING
import discord.abc
from .flags import PublicUserFlags
from .utils import snowflake_time, _bytes_to_base64_data, MISSING
from .enums import DefaultAvatar
from .colour import Colour
from .asset import Asset

__all__ = (
    'User',
    'ClientUser',
)

class _UserTag:
    __slots__ = ()
    id: int

class BaseUser(_UserTag):
    __slots__ = ('name', 'id', 'discriminator', '_avatar', '_banner', '_accent_colour', 'bot', 'system', '_public_flags', '_state')

    if TYPE_CHECKING:
        name: str
        id: int
        discriminator: str
        bot: bool
        system: bool

    def __init__(self, *, state, data):
        self._state = state
        self._update(data)

    def __repr__(self):
        return (
            f"<BaseUser id={self.id} name={self.name!r} discriminator={self.discriminator!r}"
            f" bot={self.bot} system={self.system}>"
        )

    def __str__(self):
        return f'{self.name}#{self.discriminator}'

    def __eq__(self, other):
        return isinstance(other, _UserTag) and other.id == self.id

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return self.id >> 22

    def _update(self, data):
        self.name = data['username']
        self.id = int(data['id'])
        self.discriminator = data['discriminator']
        self._avatar = data['avatar']
        self._banner = data.get('banner', None)
        self._accent_colour = data.get('accent_color', None)
        self._public_flags = data.get('public_flags', 0)
        self.bot = data.get('bot', False)
        self.system = data.get('system', False)

    @classmethod
    def _copy(cls, user):
        self = cls.__new__(cls)  # bypass __init__

        self.name = user.name
        self.id = user.id
        self.discriminator = user.discriminator
        self._avatar = user._avatar
        self._banner = user._banner
        self._accent_colour = user._accent_colour
        self.bot = user.bot
        self._state = user._state
        self._public_flags = user._public_flags

        return self

    def _to_minimal_user_json(self):
        return {
            'username': self.name,
            'id': self.id,
            'avatar': self._avatar,
            'discriminator': self.discriminator,
            'bot': self.bot,
        }

    @property
    def public_flags(self):
        """:class:`PublicUserFlags`: The publicly available flags the user has."""
        return PublicUserFlags._from_value(self._public_flags)

    @property
    def avatar(self):
        """:class:`Asset`: Returns an :class:`Asset` for the avatar the user has.

        If the user does not have a traditional avatar, an asset for
        the default avatar is returned instead.
        """
        if self._avatar is None:
            return Asset._from_default_avatar(self._state, int(self.discriminator) % len(DefaultAvatar))
        else:
            return Asset._from_avatar(self._state, self.id, self._avatar)

    @property
    def default_avatar(self):
        """:class:`Asset`: Returns the default avatar for a given user. This is calculated by the user's discriminator."""
        return Asset._from_default_avatar(self._state, int(self.discriminator) % len(DefaultAvatar))

    @property
    def banner(self) -> Optional[Asset]:
        """Optional[:class:`Asset`]: Returns the user's banner asset, if available.

        .. versionadded:: 2.0

        .. note::
            This information is only available via :meth:`Client.fetch_user`.
        """
        if self._banner is None:
            return None
        return Asset._from_user_banner(self._state, self.id, self._banner)

    @property
    def accent_colour(self) -> Optional[Colour]:
        """Optional[:class:`Colour`]: Returns the user's accent colour, if applicable.

        There is an alias for this named :attr:`accent_color`.

        .. versionadded:: 2.0

        .. note::

            This information is only available via :meth:`Client.fetch_user`.
        """
        if self._accent_colour is None:
            return None
        return Colour(self._accent_colour)

    @property
    def accent_color(self) -> Optional[Colour]:
        """Optional[:class:`Colour`]: Returns the user's accent color, if applicable.

        There is an alias for this named :attr:`accent_colour`.

        .. versionadded:: 2.0

        .. note::

            This information is only available via :meth:`Client.fetch_user`.
        """
        return self.accent_colour

    @property
    def colour(self):
        """:class:`Colour`: A property that returns a colour denoting the rendered colour
        for the user. This always returns :meth:`Colour.default`.

        There is an alias for this named :attr:`color`.
        """
        return Colour.default()

    @property
    def color(self):
        """:class:`Colour`: A property that returns a color denoting the rendered color
        for the user. This always returns :meth:`Colour.default`.

        There is an alias for this named :attr:`colour`.
        """
        return self.colour

    @property
    def mention(self):
        """:class:`str`: Returns a string that allows you to mention the given user."""
        return f'<@{self.id}>'

    @property
    def created_at(self):
        """:class:`datetime.datetime`: Returns the user's creation time in UTC.

        This is when the user's Discord account was created.
        """
        return snowflake_time(self.id)

    @property
    def display_name(self):
        """:class:`str`: Returns the user's display name.

        For regular users this is just their username, but
        if they have a guild specific nickname then that
        is returned instead.
        """
        return self.name

    def mentioned_in(self, message):
        """Checks if the user is mentioned in the specified message.

        Parameters
        -----------
        message: :class:`Message`
            The message to check if you're mentioned in.

        Returns
        -------
        :class:`bool`
            Indicates if the user is mentioned in the message.
        """

        if message.mention_everyone:
            return True

        return any(user.id == self.id for user in message.mentions)

class ClientUser(BaseUser):
    """Represents your Discord user.

    .. container:: operations

        .. describe:: x == y

            Checks if two users are equal.

        .. describe:: x != y

            Checks if two users are not equal.

        .. describe:: hash(x)

            Return the user's hash.

        .. describe:: str(x)

            Returns the user's name with discriminator.

    Attributes
    -----------
    name: :class:`str`
        The user's username.
    id: :class:`int`
        The user's unique ID.
    discriminator: :class:`str`
        The user's discriminator. This is given when the username has conflicts.
    bot: :class:`bool`
        Specifies if the user is a bot account.
    system: :class:`bool`
        Specifies if the user is a system user (i.e. represents Discord officially).

        .. versionadded:: 1.3

    verified: :class:`bool`
        Specifies if the user's email is verified.
    locale: Optional[:class:`str`]
        The IETF language tag used to identify the language the user is using.
    mfa_enabled: :class:`bool`
        Specifies if the user has MFA turned on and working.
    """

    __slots__ = ('locale', '_flags', 'verified', 'mfa_enabled', '__weakref__')

    def __init__(self, *, state, data):
        super().__init__(state=state, data=data)

    def __repr__(self):
        return (
            f'<ClientUser id={self.id} name={self.name!r} discriminator={self.discriminator!r}'
            f' bot={self.bot} verified={self.verified} mfa_enabled={self.mfa_enabled}>'
        )

    def _update(self, data):
        super()._update(data)
        # There's actually an Optional[str] phone field as well but I won't use it
        self.verified = data.get('verified', False)
        self.locale = data.get('locale')
        self._flags = data.get('flags', 0)
        self.mfa_enabled = data.get('mfa_enabled', False)

    async def edit(self, *, username: str = MISSING, avatar: bytes = MISSING) -> None:
        """|coro|

        Edits the current profile of the client.

        .. note::

            To upload an avatar, a :term:`py:bytes-like object` must be passed in that
            represents the image being uploaded. If this is done through a file
            then the file must be opened via ``open('some_filename', 'rb')`` and
            the :term:`py:bytes-like object` is given through the use of ``fp.read()``.

            The only image formats supported for uploading is JPEG and PNG.

        Parameters
        -----------
        username: :class:`str`
            The new username you wish to change to.
        avatar: :class:`bytes`
            A :term:`py:bytes-like object` representing the image to upload.
            Could be ``None`` to denote no avatar.

        Raises
        ------
        HTTPException
            Editing your profile failed.
        InvalidArgument
            Wrong image format passed for ``avatar``.
        """
        payload: Dict[str, Any] = {}
        if username is not MISSING:
            payload['username'] = username

        if avatar is not MISSING:
            payload['avatar'] = _bytes_to_base64_data(avatar)

        data = await self._state.http.edit_profile(payload)
        self._update(data)

class User(BaseUser, discord.abc.Messageable):
    """Represents a Discord user.

    .. container:: operations

        .. describe:: x == y

            Checks if two users are equal.

        .. describe:: x != y

            Checks if two users are not equal.

        .. describe:: hash(x)

            Return the user's hash.

        .. describe:: str(x)

            Returns the user's name with discriminator.

    Attributes
    -----------
    name: :class:`str`
        The user's username.
    id: :class:`int`
        The user's unique ID.
    discriminator: :class:`str`
        The user's discriminator. This is given when the username has conflicts.
    bot: :class:`bool`
        Specifies if the user is a bot account.
    system: :class:`bool`
        Specifies if the user is a system user (i.e. represents Discord officially).
    """

    __slots__ = ('_stored',)

    def __init__(self, *, state, data):
        super().__init__(state=state, data=data)
        self._stored = False

    def __repr__(self):
        return f'<User id={self.id} name={self.name!r} discriminator={self.discriminator!r} bot={self.bot}>'

    def __del__(self) -> None:
        try:
            if self._stored:
                self._state.deref_user(self.id)
        except Exception:
            pass

    @classmethod
    def _copy(cls, user):
        self = super()._copy(user)
        self._stored = False
        return self

    async def _get_channel(self):
        ch = await self.create_dm()
        return ch

    @property
    def dm_channel(self):
        """Optional[:class:`DMChannel`]: Returns the channel associated with this user if it exists.

        If this returns ``None``, you can create a DM channel by calling the
        :meth:`create_dm` coroutine function.
        """
        return self._state._get_private_channel_by_user(self.id)

    @property
    def mutual_guilds(self):
        """List[:class:`Guild`]: The guilds that the user shares with the client.

        .. note::

            This will only return mutual guilds within the client's internal cache.

        .. versionadded:: 1.7
        """
        return [guild for guild in self._state._guilds.values() if guild.get_member(self.id)]

    async def create_dm(self):
        """|coro|

        Creates a :class:`DMChannel` with this user.

        This should be rarely called, as this is done transparently for most
        people.

        Returns
        -------
        :class:`.DMChannel`
            The channel that was created.
        """
        found = self.dm_channel
        if found is not None:
            return found

        state = self._state
        data = await state.http.start_private_message(self.id)
        return state.add_dm_channel(data)
<commit_msg>Type-hint user.py<commit_after>"""
The MIT License (MIT)

Copyright (c) 2015-present Rapptz

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional, Type, TypeVar, TYPE_CHECKING

import discord.abc
from .asset import Asset
from .colour import Colour
from .enums import DefaultAvatar
from .flags import PublicUserFlags
from .utils import snowflake_time, _bytes_to_base64_data, MISSING

if TYPE_CHECKING:
    from datetime import datetime

    from .channel import DMChannel
    from .guild import Guild
    from .message import Message
    from .state import ConnectionState
    from .types.channel import DMChannel as DMChannelPayload
    from .types.user import User as UserPayload

__all__ = (
    'User',
    'ClientUser',
)

U = TypeVar('U', bound='User')
BU = TypeVar('BU', bound='BaseUser')

class _UserTag:
    __slots__ = ()
    id: int

class BaseUser(_UserTag):
    __slots__ = ('name', 'id', 'discriminator', '_avatar', '_banner', '_accent_colour', 'bot', 'system', '_public_flags', '_state')

    if TYPE_CHECKING:
        name: str
        id: int
        discriminator: str
        bot: bool
        system: bool
        _state: ConnectionState
        _avatar: str
        _banner: Optional[str]
        _accent_colour: Optional[str]
        _public_flags: int

    def __init__(self, *, state: ConnectionState, data: UserPayload) -> None:
        self._state = state
        self._update(data)

    def __repr__(self) -> str:
        return (
            f"<BaseUser id={self.id} name={self.name!r} discriminator={self.discriminator!r}"
            f" bot={self.bot} system={self.system}>"
        )

    def __str__(self) -> str:
        return f'{self.name}#{self.discriminator}'

    def __eq__(self, other: Any) -> bool:
        return isinstance(other, _UserTag) and other.id == self.id

    def __ne__(self, other: Any) -> bool:
        return not self.__eq__(other)

    def __hash__(self) -> int:
        return self.id >> 22

    def _update(self, data: UserPayload) -> None:
        self.name = data['username']
        self.id = int(data['id'])
        self.discriminator = data['discriminator']
        self._avatar = data['avatar']
        self._banner = data.get('banner', None)
        self._accent_colour = data.get('accent_color', None)
        self._public_flags = data.get('public_flags', 0)
        self.bot = data.get('bot', False)
        self.system = data.get('system', False)

    @classmethod
    def _copy(cls: Type[BU], user: BU) -> BU:
        self = cls.__new__(cls)  # bypass __init__

        self.name = user.name
        self.id = user.id
        self.discriminator = user.discriminator
        self._avatar = user._avatar
        self._banner = user._banner
        self._accent_colour = user._accent_colour
        self.bot = user.bot
        self._state = user._state
        self._public_flags = user._public_flags

        return self

    def _to_minimal_user_json(self) -> Dict[str, Any]:
        return {
            'username': self.name,
            'id': self.id,
            'avatar': self._avatar,
            'discriminator': self.discriminator,
            'bot': self.bot,
        }

    @property
    def public_flags(self) -> PublicUserFlags:
        """:class:`PublicUserFlags`: The publicly available flags the user has."""
        return PublicUserFlags._from_value(self._public_flags)

    @property
    def avatar(self) -> Asset:
        """:class:`Asset`: Returns an :class:`Asset` for the avatar the user has.

        If the user does not have a traditional avatar, an asset for
        the default avatar is returned instead.
        """
        if self._avatar is None:
            return Asset._from_default_avatar(self._state, int(self.discriminator) % len(DefaultAvatar))
        else:
            return Asset._from_avatar(self._state, self.id, self._avatar)

    @property
    def default_avatar(self) -> Asset:
        """:class:`Asset`: Returns the default avatar for a given user. This is calculated by the user's discriminator."""
        return Asset._from_default_avatar(self._state, int(self.discriminator) % len(DefaultAvatar))

    @property
    def banner(self) -> Optional[Asset]:
        """Optional[:class:`Asset`]: Returns the user's banner asset, if available.

        .. versionadded:: 2.0

        .. note::
            This information is only available via :meth:`Client.fetch_user`.
        """
        if self._banner is None:
            return None
        return Asset._from_user_banner(self._state, self.id, self._banner)

    @property
    def accent_colour(self) -> Optional[Colour]:
        """Optional[:class:`Colour`]: Returns the user's accent colour, if applicable.

        There is an alias for this named :attr:`accent_color`.

        .. versionadded:: 2.0

        .. note::

            This information is only available via :meth:`Client.fetch_user`.
        """
        if self._accent_colour is None:
            return None
        return Colour(self._accent_colour)

    @property
    def accent_color(self) -> Optional[Colour]:
        """Optional[:class:`Colour`]: Returns the user's accent color, if applicable.

        There is an alias for this named :attr:`accent_colour`.

        .. versionadded:: 2.0

        .. note::

            This information is only available via :meth:`Client.fetch_user`.
        """
        return self.accent_colour

    @property
    def colour(self) -> Colour:
        """:class:`Colour`: A property that returns a colour denoting the rendered colour
        for the user. This always returns :meth:`Colour.default`.

        There is an alias for this named :attr:`color`.
        """
        return Colour.default()

    @property
    def color(self) -> Colour:
        """:class:`Colour`: A property that returns a color denoting the rendered color
        for the user. This always returns :meth:`Colour.default`.

        There is an alias for this named :attr:`colour`.
        """
        return self.colour

    @property
    def mention(self) -> str:
        """:class:`str`: Returns a string that allows you to mention the given user."""
        return f'<@{self.id}>'

    @property
    def created_at(self) -> datetime:
        """:class:`datetime.datetime`: Returns the user's creation time in UTC.

        This is when the user's Discord account was created.
        """
        return snowflake_time(self.id)

    @property
    def display_name(self) -> str:
        """:class:`str`: Returns the user's display name.

        For regular users this is just their username, but
        if they have a guild specific nickname then that
        is returned instead.
        """
        return self.name

    def mentioned_in(self, message: Message) -> bool:
        """Checks if the user is mentioned in the specified message.

        Parameters
        -----------
        message: :class:`Message`
            The message to check if you're mentioned in.

        Returns
        -------
        :class:`bool`
            Indicates if the user is mentioned in the message.
        """

        if message.mention_everyone:
            return True

        return any(user.id == self.id for user in message.mentions)

class ClientUser(BaseUser):
    """Represents your Discord user.

    .. container:: operations

        .. describe:: x == y

            Checks if two users are equal.

        .. describe:: x != y

            Checks if two users are not equal.

        .. describe:: hash(x)

            Return the user's hash.

        .. describe:: str(x)

            Returns the user's name with discriminator.

    Attributes
    -----------
    name: :class:`str`
        The user's username.
    id: :class:`int`
        The user's unique ID.
    discriminator: :class:`str`
        The user's discriminator. This is given when the username has conflicts.
    bot: :class:`bool`
        Specifies if the user is a bot account.
    system: :class:`bool`
        Specifies if the user is a system user (i.e. represents Discord officially).

        .. versionadded:: 1.3

    verified: :class:`bool`
        Specifies if the user's email is verified.
    locale: Optional[:class:`str`]
        The IETF language tag used to identify the language the user is using.
    mfa_enabled: :class:`bool`
        Specifies if the user has MFA turned on and working.
    """

    __slots__ = ('locale', '_flags', 'verified', 'mfa_enabled', '__weakref__')

    if TYPE_CHECKING:
        verified: bool
        local: Optional[str]
        mfa_enabled: bool
        _flags: int

    def __init__(self, *, state: ConnectionState, data: UserPayload) -> None:
        super().__init__(state=state, data=data)

    def __repr__(self) -> str:
        return (
            f'<ClientUser id={self.id} name={self.name!r} discriminator={self.discriminator!r}'
            f' bot={self.bot} verified={self.verified} mfa_enabled={self.mfa_enabled}>'
        )

    def _update(self, data: UserPayload) -> None:
        super()._update(data)
        # There's actually an Optional[str] phone field as well but I won't use it
        self.verified = data.get('verified', False)
        self.locale = data.get('locale')
        self._flags = data.get('flags', 0)
        self.mfa_enabled = data.get('mfa_enabled', False)

    async def edit(self, *, username: str = MISSING, avatar: bytes = MISSING) -> None:
        """|coro|

        Edits the current profile of the client.

        .. note::

            To upload an avatar, a :term:`py:bytes-like object` must be passed in that
            represents the image being uploaded. If this is done through a file
            then the file must be opened via ``open('some_filename', 'rb')`` and
            the :term:`py:bytes-like object` is given through the use of ``fp.read()``.

            The only image formats supported for uploading is JPEG and PNG.

        Parameters
        -----------
        username: :class:`str`
            The new username you wish to change to.
        avatar: :class:`bytes`
            A :term:`py:bytes-like object` representing the image to upload.
            Could be ``None`` to denote no avatar.

        Raises
        ------
        HTTPException
            Editing your profile failed.
        InvalidArgument
            Wrong image format passed for ``avatar``.
        """
        payload: Dict[str, Any] = {}
        if username is not MISSING:
            payload['username'] = username

        if avatar is not MISSING:
            payload['avatar'] = _bytes_to_base64_data(avatar)

        data: UserPayload = await self._state.http.edit_profile(payload)
        self._update(data)

class User(BaseUser, discord.abc.Messageable):
    """Represents a Discord user.

    .. container:: operations

        .. describe:: x == y

            Checks if two users are equal.

        .. describe:: x != y

            Checks if two users are not equal.

        .. describe:: hash(x)

            Return the user's hash.

        .. describe:: str(x)

            Returns the user's name with discriminator.

    Attributes
    -----------
    name: :class:`str`
        The user's username.
    id: :class:`int`
        The user's unique ID.
    discriminator: :class:`str`
        The user's discriminator. This is given when the username has conflicts.
    bot: :class:`bool`
        Specifies if the user is a bot account.
    system: :class:`bool`
        Specifies if the user is a system user (i.e. represents Discord officially).
    """

    __slots__ = ('_stored',)

    if TYPE_CHECKING:
        _stored: bool

    def __init__(self, *, state: ConnectionState, data: UserPayload) -> None:
        super().__init__(state=state, data=data)
        self._stored = False

    def __repr__(self) -> str:
        return f'<User id={self.id} name={self.name!r} discriminator={self.discriminator!r} bot={self.bot}>'

    def __del__(self) -> None:
        try:
            if self._stored:
                self._state.deref_user(self.id)
        except Exception:
            pass

    @classmethod
    def _copy(cls: Type[U], user: U) -> U:
        self = super()._copy(user)
        self._stored = False
        return self

    async def _get_channel(self) -> DMChannel:
        ch = await self.create_dm()
        return ch

    @property
    def dm_channel(self) -> Optional[DMChannel]:
        """Optional[:class:`DMChannel`]: Returns the channel associated with this user if it exists.

        If this returns ``None``, you can create a DM channel by calling the
        :meth:`create_dm` coroutine function.
        """
        return self._state._get_private_channel_by_user(self.id)

    @property
    def mutual_guilds(self) -> List[Guild]:
        """List[:class:`Guild`]: The guilds that the user shares with the client.

        .. note::

            This will only return mutual guilds within the client's internal cache.

        .. versionadded:: 1.7
        """
        return [guild for guild in self._state._guilds.values() if guild.get_member(self.id)]

    async def create_dm(self) -> DMChannel:
        """|coro|

        Creates a :class:`DMChannel` with this user.

        This should be rarely called, as this is done transparently for most
        people.

        Returns
        -------
        :class:`.DMChannel`
            The channel that was created.
        """
        found = self.dm_channel
        if found is not None:
            return found

        state = self._state
        data: DMChannelPayload = await state.http.start_private_message(self.id)
        return state.add_dm_channel(data)
<|endoftext|>


<commit_before>
from discord.ext import commands

import difflib

from utilities import checks

def setup(bot):
	bot.add_cog(Blobs(bot))

class Blobs(commands.Cog):

	def __init__(self, bot):
		self.bot = bot
		self.bot.loop.create_task(self.initialize_database(), name = "Initialize database")

	async def initialize_database(self):
		await self.bot.connect_to_database()
		await self.bot.db.execute("CREATE SCHEMA IF NOT EXISTS blobs")
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.blobs (
				blob	TEXT PRIMARY KEY,
				image	TEXT
			)
			"""
		)
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.aliases (
				alias	TEXT PRIMARY KEY,
				blob	TEXT REFERENCES blobs.blobs(blob) ON DELETE CASCADE
			)
			"""
		)
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.stats (
				blob			TEXT REFERENCES blobs.blobs(blob) ON DELETE CASCADE,
				user_id			BIGINT,
				count			BIGINT,
				PRIMARY KEY		(blob, user_id)
			)
			"""
		)

	@commands.group(aliases = ["blob"], invoke_without_command = True, case_insensitive = True)
	@checks.not_forbidden()
	async def blobs(self, ctx, *, blob : str):
		'''Blob/Google Emoji'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs UNION SELECT alias FROM blobs.aliases")
		blob_names = [record["blob"] for record in records]
		close_match = difflib.get_close_matches(blob, blob_names, n = 1)
		if not close_match:
			return await ctx.embed_reply(":no_entry: Blob not found")
		blob = close_match[0]
		image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", blob)
		if not image_url:
			blob = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", blob)
			image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", blob)
		await ctx.embed_reply(title = blob, image_url = image_url)
		await ctx.bot.db.execute(
			"""
			INSERT INTO blobs.stats (blob, user_id, count)
			VALUES ($1, $2, 1)
			ON CONFLICT (blob, user_id) DO
			UPDATE SET count = stats.count + 1
			""",
			blob, ctx.author.id
		)

	@blobs.command(aliases = ["edit"])
	@commands.is_owner()
	async def add(self, ctx, name : str, image_url : str, *aliases : str):
		'''Add or edit a blob'''
		await ctx.bot.db.execute(
			"""
			INSERT INTO blobs.blobs (blob, image)
			VALUES ($1, $2)
			ON CONFLICT (blob) DO
			UPDATE SET image = $2
			""",
			name, image_url
		)
		await ctx.bot.db.execute("DELETE FROM blobs.aliases WHERE blob = $1", name)
		for alias in aliases:
			inserted = await ctx.bot.db.fetchrow(
				"""
				INSERT INTO blobs.aliases (alias, blob)
				VALUES ($1, $2)
				ON CONFLICT DO NOTHING
				RETURNING *
				""",
				alias, name
			)
			if not inserted:
				await ctx.embed_reply(f"Failed to add already existing alias: {alias}")
		await ctx.embed_reply("Blob added/edited")

	@blobs.command(aliases = ["details"])
	@commands.is_owner()
	async def info(self, ctx, name : str):
		'''Information about a blob'''
		image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", name)
		if not image_url:
			name = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", name)
			if not name:
				return await ctx.embed_reply(f":no_entry: Blob not found")
			image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", name)
		records = await ctx.bot.db.fetch("SELECT alias FROM blobs.aliases WHERE blob = $1", name)
		aliases = [record["alias"] for record in records]
		await ctx.embed_reply(image_url, title = name, fields = (("Aliases", ", ".join(aliases) or "None"),))

	@blobs.command()
	@checks.not_forbidden()
	async def list(self, ctx):
		'''List blobs'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs")
		blob_names = [record["blob"] for record in records]
		await ctx.embed_reply(", ".join(sorted(blob_names)))

	@blobs.command(aliases = ["delete"])
	@commands.is_owner()
	async def remove(self, ctx, name : str):
		'''Remove a blob'''
		await ctx.bot.db.execute("DELETE FROM blobs.blobs WHERE blob = $1", name)
		await ctx.embed_reply("Blob removed")

	@blobs.command()
	@checks.not_forbidden()
	async def stats(self, ctx, *, blob : str):
		'''Blob emoji stats'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs UNION SELECT alias FROM blobs.aliases")
		blob_names = [record["blob"] for record in records]
		close_match = difflib.get_close_matches(blob, blob_names, n = 1)
		if not close_match:
			return await ctx.embed_reply(":no_entry: Blob not found")
		blob = close_match[0]
		records = await ctx.bot.db.fetch("SELECT user_id, count FROM blobs.stats WHERE blob = $1", blob)
		if not records:
			blob = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", blob)
			records = await ctx.bot.db.fetch("SELECT user_id, count FROM blobs.stats WHERE blob = $1", blob)
		personal = 0
		total = 0
		for record in records:
			if record["user_id"] == ctx.author.id:
				personal = record["count"]
			total += record["count"]
		await ctx.embed_reply(f"Personal: {personal}\nTotal: {total}")

	@blobs.command()
	@checks.not_forbidden()
	async def top(self, ctx):
		'''Top blob emoji'''
		records = await ctx.bot.db.fetch(
			"""
			SELECT blob, count
			FROM blobs.stats
			WHERE user_id = $1
			ORDER BY count
			DESC LIMIT 5
			""",
			ctx.author.id
		)
		personal = [f"{count}. {record['blob']} ({record['count']})" for count, record in enumerate(records, start = 1)]
		records = await ctx.bot.db.fetch(
			"""
			SELECT blob, SUM(count) as count
			FROM blobs.stats
			GROUP BY blob
			ORDER BY SUM(count)
			DESC LIMIT 5
			"""
		)
		total = [f"{count}. {record['blob']} ({record['count']})" for count, record in enumerate(records, start = 1)]
		await ctx.embed_reply(fields = (("Personal", '\n'.join(personal)), ("Total", '\n'.join(total))))

<commit_msg>[Discord] Handle too many blobs for blobs list command<commit_after>
from discord.ext import commands

import difflib
import textwrap

from utilities import checks

def setup(bot):
	bot.add_cog(Blobs(bot))

class Blobs(commands.Cog):

	def __init__(self, bot):
		self.bot = bot
		self.bot.loop.create_task(self.initialize_database(), name = "Initialize database")

	async def initialize_database(self):
		await self.bot.connect_to_database()
		await self.bot.db.execute("CREATE SCHEMA IF NOT EXISTS blobs")
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.blobs (
				blob	TEXT PRIMARY KEY,
				image	TEXT
			)
			"""
		)
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.aliases (
				alias	TEXT PRIMARY KEY,
				blob	TEXT REFERENCES blobs.blobs(blob) ON DELETE CASCADE
			)
			"""
		)
		await self.bot.db.execute(
			"""
			CREATE TABLE IF NOT EXISTS blobs.stats (
				blob			TEXT REFERENCES blobs.blobs(blob) ON DELETE CASCADE,
				user_id			BIGINT,
				count			BIGINT,
				PRIMARY KEY		(blob, user_id)
			)
			"""
		)

	@commands.group(aliases = ["blob"], invoke_without_command = True, case_insensitive = True)
	@checks.not_forbidden()
	async def blobs(self, ctx, *, blob : str):
		'''Blob/Google Emoji'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs UNION SELECT alias FROM blobs.aliases")
		blob_names = [record["blob"] for record in records]
		close_match = difflib.get_close_matches(blob, blob_names, n = 1)
		if not close_match:
			return await ctx.embed_reply(":no_entry: Blob not found")
		blob = close_match[0]
		image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", blob)
		if not image_url:
			blob = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", blob)
			image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", blob)
		await ctx.embed_reply(title = blob, image_url = image_url)
		await ctx.bot.db.execute(
			"""
			INSERT INTO blobs.stats (blob, user_id, count)
			VALUES ($1, $2, 1)
			ON CONFLICT (blob, user_id) DO
			UPDATE SET count = stats.count + 1
			""",
			blob, ctx.author.id
		)

	@blobs.command(aliases = ["edit"])
	@commands.is_owner()
	async def add(self, ctx, name : str, image_url : str, *aliases : str):
		'''Add or edit a blob'''
		await ctx.bot.db.execute(
			"""
			INSERT INTO blobs.blobs (blob, image)
			VALUES ($1, $2)
			ON CONFLICT (blob) DO
			UPDATE SET image = $2
			""",
			name, image_url
		)
		await ctx.bot.db.execute("DELETE FROM blobs.aliases WHERE blob = $1", name)
		for alias in aliases:
			inserted = await ctx.bot.db.fetchrow(
				"""
				INSERT INTO blobs.aliases (alias, blob)
				VALUES ($1, $2)
				ON CONFLICT DO NOTHING
				RETURNING *
				""",
				alias, name
			)
			if not inserted:
				await ctx.embed_reply(f"Failed to add already existing alias: {alias}")
		await ctx.embed_reply("Blob added/edited")

	@blobs.command(aliases = ["details"])
	@commands.is_owner()
	async def info(self, ctx, name : str):
		'''Information about a blob'''
		image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", name)
		if not image_url:
			name = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", name)
			if not name:
				return await ctx.embed_reply(f":no_entry: Blob not found")
			image_url = await ctx.bot.db.fetchval("SELECT image FROM blobs.blobs WHERE blob = $1", name)
		records = await ctx.bot.db.fetch("SELECT alias FROM blobs.aliases WHERE blob = $1", name)
		aliases = [record["alias"] for record in records]
		await ctx.embed_reply(image_url, title = name, fields = (("Aliases", ", ".join(aliases) or "None"),))

	@blobs.command()
	@checks.not_forbidden()
	async def list(self, ctx, offset: int = 0):
		'''List blobs'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs")
		blob_names = [record["blob"] for record in records]
		await ctx.embed_reply(textwrap.shorten(", ".join(sorted(blob_names)[offset:]),
												width = ctx.bot.EMBED_DESCRIPTION_CHARACTER_LIMIT,
												placeholder = " ..."))

	@blobs.command(aliases = ["delete"])
	@commands.is_owner()
	async def remove(self, ctx, name : str):
		'''Remove a blob'''
		await ctx.bot.db.execute("DELETE FROM blobs.blobs WHERE blob = $1", name)
		await ctx.embed_reply("Blob removed")

	@blobs.command()
	@checks.not_forbidden()
	async def stats(self, ctx, *, blob : str):
		'''Blob emoji stats'''
		records = await ctx.bot.db.fetch("SELECT blob FROM blobs.blobs UNION SELECT alias FROM blobs.aliases")
		blob_names = [record["blob"] for record in records]
		close_match = difflib.get_close_matches(blob, blob_names, n = 1)
		if not close_match:
			return await ctx.embed_reply(":no_entry: Blob not found")
		blob = close_match[0]
		records = await ctx.bot.db.fetch("SELECT user_id, count FROM blobs.stats WHERE blob = $1", blob)
		if not records:
			blob = await ctx.bot.db.fetchval("SELECT blob FROM blobs.aliases WHERE alias = $1", blob)
			records = await ctx.bot.db.fetch("SELECT user_id, count FROM blobs.stats WHERE blob = $1", blob)
		personal = 0
		total = 0
		for record in records:
			if record["user_id"] == ctx.author.id:
				personal = record["count"]
			total += record["count"]
		await ctx.embed_reply(f"Personal: {personal}\nTotal: {total}")

	@blobs.command()
	@checks.not_forbidden()
	async def top(self, ctx):
		'''Top blob emoji'''
		records = await ctx.bot.db.fetch(
			"""
			SELECT blob, count
			FROM blobs.stats
			WHERE user_id = $1
			ORDER BY count
			DESC LIMIT 5
			""",
			ctx.author.id
		)
		personal = [f"{count}. {record['blob']} ({record['count']})" for count, record in enumerate(records, start = 1)]
		records = await ctx.bot.db.fetch(
			"""
			SELECT blob, SUM(count) as count
			FROM blobs.stats
			GROUP BY blob
			ORDER BY SUM(count)
			DESC LIMIT 5
			"""
		)
		total = [f"{count}. {record['blob']} ({record['count']})" for count, record in enumerate(records, start = 1)]
		await ctx.embed_reply(fields = (("Personal", '\n'.join(personal)), ("Total", '\n'.join(total))))

<|endoftext|>