Example #1
0
 def make_catalogs_or_indexes_change_ans(self, qt: str, answer: Answer, chain: TranslationChain, result: Result):
     tag = '目录' if qt == 'catalogs_change' else '指标'
     data = self._search_direct(chain)
     y = [len(item) for item in data]
     line = self.painter.paint_line(result['year'], f'{tag}个数', y, result.raw_question)
     answer.save_chart(line)
     answer.add_answer(f'该问题的回答已渲染为图像,详见:{CHART_RENDER_DIR}/{result.raw_question}.html')
Example #2
0
 def make_begin_stats_ans(self, answer: Answer, builder: AnswerBuilder,
                          chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     builder.feed_data(data)
     for item, name in builder.product_data_with_name(result['index']):
         years = [int(year.name) for year in item]
         answer.add_answer(f'指标“{name.name}”最早于{min(years)}年开始统计')
Example #3
0
 def make_indexes_or_areas_trend_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                     chain: TranslationChain, result: Result, mark_point: bool = False):
     if qt == 'areas_trend':
         unpack = True
         gen = [result['area'], result['index']]
     else:
         unpack = False
         gen = [result['index']]
     # collect
     data = self._search_direct(chain, unpack=unpack)
     builder.feed_data(data)
     collects = []  # 根据不同单位划分数据
     for item, name in builder.product_data_with_name(*gen):
         collect = []
         units = set([n.unit for n in item if n.unit != ''])
         ys = builder.group_mapping_to_float(item)
         if builder.add_if_is_equal_or_not(sum(ys), 0, equal=False,
                                           no=f'指标“{name.subject()}”无任何值记录,无法比较'):
             for unit in units:
                 tmp = []
                 for y, n in zip(ys, item):
                     tmp.append(y if n.unit == unit else 0)
                 collect.append((name.subject(), unit, tmp))
             collects.append(collect)
     # paint
     if len(collects) != 0:
         bar = self.painter.paint_bar(result['year'], collects,
                                      title=result.raw_question, mark_point=mark_point)
         answer.save_chart(bar)
         answer.add_answer(f'该问题的回答已渲染为图像,详见:{CHART_RENDER_DIR}/{result.raw_question}.html')
Example #4
0
 def make_index_value_ans(self, answer: Answer, builder: AnswerBuilder,
                          chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     builder.feed_data(data)
     for item, name in builder.product_data_with_name(
             result['index'], if_is_none=lambda _, na: f'无{na.subject()}数据记录'
     ):
         answer.add_answer(f'{name.subject()}为{item.val()}')
Example #5
0
 def make_catalog_status_ans(self, answer: Answer, builder: AnswerBuilder,
                             chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     builder.feed_data(data)
     for item, name in builder.product_data_with_name(
             result['catalog'],
             if_is_none=lambda _, na: f'并没有关于{result["year"][0]}年{na.subject()}的描述'
     ):
         answer.add_answer(item.info)
Example #6
0
 def __init__(self):
     self._answer = Answer()
     self._question = ''
     self._scores = {}
     self._clue_number = 0
     self._admins = list(config.ADMINS)
     self._game_channel = config.GAME_CHANNEL
     self._current_points = 5
     self._questions_dir = config.Q_DIR
     self._lc = LoopingCall(self._play_game)
     self._load_game()
     self._votes = 0
     self._voters = []
Example #7
0
 def make_catalog_or_index_change_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                      chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     tag_name = '指标' if qt == 'index_change' else '目录'
     set1, set2 = set([n.name for n in data[0]]), set([n.name for n in data[1]])
     diff1, diff2 = set1.difference(set2), set2.difference(set1)
     n1, n2 = len(diff1), len(diff2)
     if builder.add_if_is_equal_or_not(
             n1, 0, equal=False,
             no=f'{result["year"][1]}年与{result["year"][0]}年的{tag_name}相同'
     ):
         answer.add_answer(f'{result["year"][1]}年与{result["year"][0]}年相比,未统计{n1}个{tag_name}:' + '、'.join(diff1))
     if builder.add_if_is_equal_or_not(
             n2, 0, equal=False,
             no=f'{result["year"][0]}年与{result["year"][1]}年的{tag_name}相同'
     ):
         answer.add_answer(f'{result["year"][0]}年与{result["year"][1]}年相比,未统计{n2}个{tag_name}:' + '、'.join(diff2))
Example #8
0
 def make_areas_g_compare_ans(self, answer: Answer, builder: AnswerBuilder,
                              chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     builder.feed_data(data)
     for item, name in builder.product_data_with_name(result['area'], result['index']):
         x, y = item
         if builder.binary_decision(
                 x, y,
                 not_x=f'无{result["year"][0]}年关于{name.subject()}的数据',
                 not_y=f'无{result["year"][0]}前一年关于{name.subject()}的数据'
         ):
             res = builder.growth_calculation(y.value, x.value)
             if builder.add_if_is_not_none(
                     res, to_sub=False,
                     no=f'{result["year"][0]}年{name.subject()}的记录非数值类型,无法计算'
             ):
                 answer.add_answer(f'{result["year"][0]}年的{name.subject()}为{y.val()},'
                                   f'其去年的为{x.val()},同比{sign(res, ("减少", "增长"))}{abs(res)}%')
Example #9
0
 def __init__(self):
     self._answer = Answer()
     self._question = ''
     self._scores = {}
     self._clue_number = 0
     self._admins = list(ADMINS)
     self._game_channel = GAME_CHANNEL
     self._current_points = 5
     self._questions_dir = Q_DIR
     self._lc = LoopingCall(self._play_game)
     self._load_game()
Example #10
0
 def __init__(self):
     self._answer = Answer()
     self._question = ''
     self._category = ''
     self._scores = {}
     self._clue_number = 0
     self._admins = list(config.ADMINS)
     self._game_channel = config.GAME_CHANNEL
     self._current_points = 5
     self._questions_dir = config.Q_DIR
     self._lc = LoopingCall(self._play_game)
     self._load_game()
     self._votes = 0
     self._voters = []
     self._newQuestionMap = {}
Example #11
0
 def make_indexes_or_areas_overall_trend_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                             chain: TranslationChain, result: Result):
     if qt == 'areas_overall_trend':
         unpack = True
         gen = [result["area"], result["index"]]
     else:
         unpack = False
         gen = [result["index"]]
     data = self._search_double_direct_then_feed(chain, unpack)
     builder.feed_data(data)
     parents = {}
     children = {}
     for x, y, f, n in builder.product_data_with_feed(
             *gen,
             if_x_is_none=lambda _1, _2, _3, na: f'无关于”{na.subject()}“的记录',
             if_y_is_none=lambda _1, _2, _3, na: f'无关于”{na.subject()}“的父级记录'
     ):
         xs = builder.group_mapping_to_float(x)
         if not builder.add_if_is_not_none(xs, to_sub=False,
                                           no=f'{n.subject()}的记录非数值类型,无法比较'):
             return
         parent = f.name + n.name if qt == 'areas_overall_trend' else f.name
         ys = builder.group_mapping_to_float(y)
         if not builder.add_if_is_not_none(ys, to_sub=False,
                                           no=f'{n.subject()}的父级记录({parent})非数值类型,无法比较'):
             return
         overall = [round(i / j, 3) if j != 0 else 0 for i, j in zip(xs, ys)]
         # 同一个父级指标将其子孙合并
         parents[parent] = ys
         children.setdefault((parent, x[-1].unit), []).append((n.subject(), xs, overall))
     # paint
     if len(parents) != 0:
         for bar in self.painter.paint_bar_stack_with_line(result['year'], children, parents,
                                                           result.raw_question):
             answer.save_chart(bar)
         answer.add_answer(f'该问题的回答已渲染为图像,详见:{CHART_RENDER_DIR}/{result.raw_question}.html')
Example #12
0
 def __init__(self):
     self._answer = Answer()
     self._question = ''
     self._scores = {}
     self._clue_number = 0
     self._admins = list(config.ADMINS)
     self._admins.append(config.OWNER)
     self._game_channel = config.GAME_CHANNEL
     self._current_points = 5
     self._questions_dir = config.Q_DIR
     self._lc = LoopingCall(self._play_game)
     self._quit = False
     self._restarting = False
     self._load_game()
     self._votes = 0
     self._voters = []
Example #13
0
 def test_masking(self):
     answer = Answer("test")
     self.assertEqual(answer.current_clue(), "****")
Example #14
0
 def test_masking(self):
     answer = Answer("test")
     self.assertEqual(answer.current_clue(), "****")
Example #15
0
class triviabot(irc.IRCClient):
    """
    This is the irc bot portion of the trivia bot.

    It implements the whole program so is kinda big. The algorithm is
    implemented by a series of callbacks, initiated by an admin on the
    server.
    """

    def __init__(self):
        self._answer = Answer()
        self._question = ""
        self._scores = {}
        self._clue_number = 0
        self._admins = list(config.ADMINS)
        self._admins.append(config.OWNER)
        self._game_channel = config.GAME_CHANNEL
        self._current_points = 5
        self._questions_dir = config.Q_DIR
        self._lc = LoopingCall(self._play_game)
        self._quit = False
        self._restarting = False
        self._load_game()
        self._votes = 0
        self._voters = []

    def _get_nickname(self):
        return self.factory.nickname

    nickname = property(_get_nickname)

    def _get_lineRate(self):
        return self.factory.lineRate

    lineRate = property(_get_lineRate)

    def _cmsg(self, dest, msg):
        """
        Write a colorized message.
        """

        self.msg(dest, "{}{}".format(config.COLOR_CODE, msg))

    def _gmsg(self, msg):
        """
        Write a message to the channel playing the trivia game.
        """

        self._cmsg(self._game_channel, msg)

    def _play_game(self):
        """
        Implements the main loop of the game.
        """
        points = {0: 5, 1: 3, 2: 2, 3: 1}
        if self._clue_number == 0:
            self._votes = 0
            self._voters = []
            self._get_new_question()
            self._current_points = points[self._clue_number]
            # Blank line.
            self._gmsg("")
            self._gmsg("Next question:")
            self._gmsg(self._question)
            self._gmsg("Clue: {}".format(self._answer.current_clue()))
            self._clue_number += 1
        # we must be somewhere in between
        elif self._clue_number < 4:
            self._current_points = points[self._clue_number]
            self._gmsg("Question:")
            self._gmsg(self._question)
            self._gmsg("Clue: {}".format(self._answer.give_clue()))
            self._clue_number += 1
        # no one must have gotten it.
        else:
            self._gmsg("No one got it. The answer was: {}".format(self._answer.answer))
            self._clue_number = 0
            self._get_new_question()
            # self._lc.reset()

    def signedOn(self):
        """
        Actions to perform on signon to the server.
        """
        self.join(self._game_channel)
        self.msg("NickServ", "identify {}".format(config.IDENT_STRING))
        print("Signed on as {}.".format(self.nickname))
        if self.factory.running:
            self._start(None, None, None)
        else:
            self._gmsg("Welcome to {}!".format(self._game_channel))
            self._gmsg("Have an admin start the game when you are ready.")
            self._gmsg("For how to use this bot, just say ?help or")
            self._gmsg("{} help.".format(self.nickname))

    def joined(self, channel):
        """
        Callback runs when the bot joins a channel
        """
        print("Joined {}.".format((channel)))

    def privmsg(self, user, channel, msg):
        """
        Parses out each message and initiates doing the right thing
        with it.
        """
        user, temp = user.split("!")
        print(user + " : " + channel + " : " + msg)
        # need to strip out non-printable characters if present.
        printable = string.printable
        msg = "".join(filter(lambda x: x in printable, msg))

        # parses each incoming line, and sees if it's a command for the bot.
        try:
            if msg[0] == "?":
                command = msg.replace("?", "").split()[0]
                args = msg.replace("?", "").split()[1:]
                self.select_command(command, args, user, channel)
                return
            elif msg.split()[0].find(self.nickname) == 0:
                command = msg.split()[1]
                args = msg.replace(self.nickname, "").split()[2:]
                self.select_command(command, args, user, channel)
                return
            # if not, try to match the message to the answer.
            else:
                if msg.lower().strip() == self._answer.answer.lower():
                    self._winner(user, channel)
                    self._save_game()
        except Exception as e:
            print(e)
            return

    def _winner(self, user, channel):
        """
        Congratulates the winner for guessing correctly and assigns
        points appropriately, then signals that it was guessed.
        """
        if channel != self._game_channel:
            self.msg(channel, "I'm sorry, answers must be given in the game channel.")
            return
        self._gmsg("{} GOT IT!".format(user.upper()))
        self._gmsg("""If there was any doubt, the correct answer was: {}""".format(self._answer.answer))
        try:
            self._scores[user] += self._current_points
        except:
            self._scores[user] = self._current_points
        if self._current_points == 1:
            self._gmsg("{} point has been added to your score!".format(str(self._current_points)))
        else:
            self._gmsg("{} points have been added to your score!".format(str(self._current_points)))
        self._clue_number = 0
        self._get_new_question()

    def ctcpQuery(self, user, channel, msg):
        """
        Responds to ctcp requests.
        Currently just reports them.
        """
        print("CTCP recieved: " + user + ":" + channel + ": " + msg[0][0] + " " + msg[0][1])

    def _help(self, args, user, channel):
        """
        Tells people how to use the bot.
        Replies differently if you are an admin or a regular user.
        Only responds to the user since there could be a game in
        progress.
        """
        try:
            self._admins.index(user)
        except:
            self._cmsg(user, "I'm {}'s trivia bot.".format(config.OWNER))
            self._cmsg(user, "Commands: score, standings, giveclue, help, " "next, source")
            return
        self._cmsg(user, "I'm {}'s trivia bot.".format(config.OWNER))
        self._cmsg(user, "Commands: score, standings, giveclue, help, next, " "skip, source")
        self._cmsg(user, "Admin commands: die, set <user> <score>, start, stop, " "save")

    def _show_source(self, args, user, channel):
        """
        Tells people how to use the bot.
        Only responds to the user since there could be a game in
        progress.
        """
        self._cmsg(user, "My source can be found at: " "https://github.com/rawsonj/triviabot")

    def select_command(self, command, args, user, channel):
        """
        Callback that responds to commands given to the bot.

        Need to differentiate between priviledged users and regular
        users.
        """
        # set up command dicts.
        unpriviledged_commands = {
            "score": self._score,
            "help": self._help,
            "source": self._show_source,
            "standings": self._standings,
            "giveclue": self._give_clue,
            "next": self._next_vote,
            "skip": self._next_question,
        }
        priviledged_commands = {
            "die": self._die,
            "restart": self._restart,
            "set": self._set_user_score,
            "start": self._start,
            "stop": self._stop,
            "save": self._save_game,
        }
        print(command, args, user, channel)
        try:
            self._admins.index(user)
            is_admin = True
        except:
            is_admin = False

        # the following takes care of sorting out functions and
        # priviledges.
        if not is_admin and command in priviledged_commands:
            self.msg(channel, "{}: You don't tell me what to do.".format(user))
            return
        elif is_admin and command in priviledged_commands:
            priviledged_commands[command](args, user, channel)
        elif command in unpriviledged_commands:
            unpriviledged_commands[command](args, user, channel)
        else:
            self.describe(channel, "{}looks at {} oddly.".format(config.COLOR_CODE, user))

    def _next_vote(self, args, user, channel):
        """Implements user voting for the next question.

        Need to keep track of who voted, and how many votes.

        """
        if not self._lc.running:
            self._gmsg("We aren't playing right now.")
            return
        try:
            self._voters.index(user)
            self._gmsg("You already voted, {}, give someone else a chance to " "hate this question".format(user))
            return
        except:
            if self._votes < 2:
                self._votes += 1
                self._voters.append(user)
                print(self._voters)
                self._gmsg("{}, you have voted. {} more votes needed to " "skip.".format(user, str(3 - self._votes)))
            else:
                self._votes = 0
                self._voters = []
                self._next_question(None, None, None)

    def _start(self, args, user, channel):
        """
        Starts the trivia game.

        TODO: Load scores from last game, if any.
        """
        if self._lc.running:
            return
        else:
            self._lc.start(config.WAIT_INTERVAL)
            self.factory.running = True

    def _stop(self, *args):
        """
        Stops the game and thanks people for playing,
        then saves the scores.
        """
        if not self._lc.running:
            return
        else:
            self._lc.stop()
            self._gmsg("Thanks for playing trivia!")
            self._gmsg("Current rankings were:")
            self._standings(None, self._game_channel, None)
            self._gmsg("""Scores have been saved, and see you next game!""")
            self._save_game()
            self.factory.running = False

    def _save_game(self, *args):
        """
        Saves the game to the data directory.
        """
        with open(os.path.join(config.SAVE_DIR, "scores.json"), "w") as savefile:
            json.dump(self._scores, savefile)
            print("Scores have been saved.")

    def _load_game(self):
        """
        Loads the running data from previous games.
        """
        # ensure initialization
        self._scores = {}
        if not path.exists(config.SAVE_DIR):
            print("Save directory doesn't exist.")
            return
        try:
            with open(os.path.join(config.SAVE_DIR, "scores.json"), "r") as savefile:
                temp_dict = json.load(savefile)
        except:
            print("Save file doesn't exist.")
            return
        for name in temp_dict.keys():
            self._scores[str(name)] = int(temp_dict[name])
        print(self._scores)
        print("Scores loaded.")

    def _set_user_score(self, args, user, channel):
        """
        Administrative action taken to adjust scores, if needed.
        """
        try:
            self._scores[args[0]] = int(args[1])
        except:
            self._cmsg(user, args[0] + " not in scores database.")
            return
        self._cmsg(user, args[0] + " score set to " + args[1])

    def _die(self, *args):
        """
        Terminates execution of the bot.
        """
        self._quit = True
        self.quit(message="This is triviabot, signing off.")

    def _restart(self, *args):
        """
        Restarts the bot
        """
        self._restarting = True
        print("Restarting")
        self.quit(message="Triviabot restarting.")

    def connectionLost(self, reason):
        """
        Called when connection is lost
        """
        global reactor
        if self._restarting:
            try:
                execl(sys.executable, *([sys.executable] + sys.argv))
            except Exception as e:
                print("Failed to restart: {}".format(e))
        if self._quit:
            reactor.stop()

    def _score(self, args, user, channel):
        """
        Tells the user their score.
        """
        try:
            self._cmsg(user, "Your current score is: {}".format(str(self._scores[user])))
        except:
            self._cmsg(user, "You aren't in my database.")

    def _next_question(self, args, user, channel):
        """
        Administratively skips the current question.
        """
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        self._gmsg("Question has been skipped. The answer was: {}".format(self._answer.answer))
        self._clue_number = 0
        self._lc.stop()
        self._lc.start(config.WAIT_INTERVAL)

    def _standings(self, args, user, channel):
        """
        Tells the user the complete standings in the game.

        TODO: order them.
        """
        self._cmsg(user, "The current trivia standings are: ")
        sorted_scores = sorted(self._scores.iteritems(), key=lambda (k, v): (v, k), reverse=True)
        for rank, (player, score) in enumerate(sorted_scores, start=1):
            formatted_score = "{}: {}: {}".format(rank, player, score)
            self._cmsg(user, formatted_score)

    def _give_clue(self, args, user, channel):
        if not self._lc.running:
            self._gmsg("we are not playing right now.")
            return
        self._cmsg(channel, "Question: ")
        self._cmsg(channel, self._question)
        self._cmsg(channel, "Clue: " + self._answer.current_clue())

    def _get_new_question(self):
        """
        Selects a new question from the questions directory and
        sets it.
        """
        damaged_question = True
        while damaged_question:
            # randomly select file
            filename = choice(listdir(self._questions_dir))
            fd = open(os.path.join(config.Q_DIR, filename))
            lines = fd.read().splitlines()
            myline = choice(lines)
            fd.close()
            try:
                self._question, temp_answer = myline.split("`")
            except ValueError:
                print("Broken question:")
                print(myline)
                continue
            self._answer.set_answer(temp_answer.strip())
            damaged_question = False
Example #16
0
 def make_areas_m_or_n_compare_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                   chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     operator = truediv if qt == 'areas_m_compare' else sub
     builder.feed_data(data)
     for (x, y), (n1, n2) in builder.product_data_with_binary(
             result['area'], result['index'],
             if_x_is_none=lambda _1, _2, na: f'无{na[0].subject()}数据记录,无法比较',
             if_y_is_none=lambda _1, _2, na: f'无{na[1].subject()}数据记录,无法比较'
     ):
         # 单位检查
         ux, uy = x.unit or '无', y.unit or '无'
         if builder.add_if_is_equal_or_not(ux, uy,
                                           no=f'{n1.subject()}的单位({ux})与{n2.subject()}的单位({uy})不同,无法比较'):
             answer.begin_sub_answers()
             n1.repr, n2.repr = x.repr, y.repr
             answer.add_sub_answers(f'{n1.subject()}为{x.val()},{n2.subject()}为{y.val()}')
             res1 = builder.binary_calculation(x.value, y.value, operator)
             if builder.add_if_is_not_none(res1,
                                           no=f'{n1.subject()}或{n2.subject()}非数值类型,无法比较'):
                 if qt == 'areas_m_compare':
                     answer.add_sub_answers(f'前者是后者的{res1}倍')
                 else:
                     answer.add_sub_answers(f'前者比后者{sign(res1)}{abs(res1)}{ux}')
             if qt == 'areas_m_compare':
                 res2 = builder.binary_calculation(y.value, x.value, truediv)
                 if builder.add_if_is_not_none(res2,
                                               no=f'{n1.subject()}或{n2.subject()}非数值类型,无法比较'):
                     answer.add_sub_answers(f'后者是前者的{res2}倍')
             answer.end_sub_answers()
Example #17
0
 def make_exist_catalog_ans(self, answer: Answer, chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     if not all(data):
         answer.add_answer(f'无{result["year"][0]}年的记录。')
     else:
         answer.add_answer(f'{result["year"][0]}年目录包括: ' + ','.join([item.name for item in data[0]]))
Example #18
0
 def make_index_compose_ans(self, answer: Answer, builder: AnswerBuilder,
                            chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     builder.feed_data(data)
     collect = []
     units = []
     sub_titles = []
     # for overall
     sqls_overall = [sql for sql in chain.iter(3)]
     data_overall = self._search_direct(sqls_overall)
     # collect
     for total, (item, name) in zip(
             data_overall,
             builder.product_data_with_name(result['index'])
     ):  # 为使两可迭代对象同步迭代和collect不用做判空,此处不使用if_is_none参数
         if not item:
             answer.add_answer(f'指标“{name.name}”没有任何组成')
             continue
         indexes, areas = [], []
         for n in item:
             n.life_check(result['year'][0])
             if n:
                 if n.label == 'Index':
                     indexes.append(n.name)
                 else:
                     areas.append(n.name)
         if len(indexes) == 0 and len(areas) == 0:
             answer.add_answer(f'指标“{name.name}”没有任何组成')
             continue
         # for indexes
         sqls1 = [sql.format(i) for sql in chain.iter(1) for i in indexes]
         data1 = self._search_direct(sqls1)
         # for areas
         sqls2 = [sql.format(name.name, a) for sql in chain.iter(2) for a in areas]
         data2 = self._search_direct(sqls2)
         # make data pairs
         final_data = {}
         for k, v in zip(indexes + areas, data1 + data2):
             if not v:
                 continue
             if v.child_id is None:
                 continue
             try:
                 final_data.setdefault(v.child_id, []).append((k, float(v.value)))
             except ValueError or TypeError:
                 answer.add_answer(f'{name.name}中{k}的记录非数值类型,无法比较')
                 return
         # make other
         for k, v in final_data.items():
             op1 = sum([x[1] for x in v])
             op2 = float(total.value)
             if op1 < int(op2):  # 舍弃一些误差,避免图中出现极小的部分
                 final_data[k].append(('其他', round(op2 - op1, 2)))
         collect.append(final_data)
         units.append(total.unit)
         sub_titles.append(f'{name.subject()}为{total.val()},其构成分为:')
     # paint
     for pie in self.painter.paint_pie(collect, units,
                                       title=result.raw_question, sub_titles=sub_titles):
         answer.save_chart(pie)
     answer.add_answer(f'该问题的回答已渲染为图像,详见:{CHART_RENDER_DIR}/{result.raw_question}.html')
Example #19
0
class triviabot(irc.IRCClient):
    '''
    This is the irc bot portion of the trivia bot.

    It implements the whole program so is kinda big. The algorithm is
    implemented by a series of callbacks, initiated by an admin on the
    server.
    '''
    def __init__(self):
        self._answer = Answer()
        self._question = ''
        self._scores = {}
        self._userlist = {}
        self._clue_number = 0
        self._admins = list(config.ADMINS)
        self._game_channel = config.GAME_CHANNEL
        self._current_points = 5
        self._questions_dir = config.Q_DIR
        self._lc = LoopingCall(self._play_game)
        self._restarting = False
        self._quit = False
        self._load_game()
        self._votes = 0
        self._voters = []
        self._no_plays = 0

    def _get_nickname(self):
        return self.factory.nickname

    nickname = property(_get_nickname)

    def _get_realname(self):
        return self.factory.realname

    realname = property(_get_realname)

    def _get_lineRate(self):
        return self.factory.lineRate

    lineRate = property(_get_lineRate)

    def _gmsg(self, msg):
        """
        Write a message to the channel playing the trivia game.
        """
        self.msg(self._game_channel, msg)

    def _play_game(self):
        '''
        Implements the main loop of the game.
        '''
        self._points = {0: 100,
                        1: 75,
                        2: 50,
                        3: 25
                        }
        self._cluelabels= {0: 'Clue:',
                           1: '2nd Clue:',
                           2: '3rd Clue:',
                           3: 'Final Clue:'
                           }
        if self._clue_number == 0:
            self._votes = 0
            self._voters = []
            self._get_new_question()
            self._current_points = self._points[self._clue_number]
            # Blank line.
            self._gmsg("")
            self._gmsg("Next question:")
            self._gmsg(self._question)
            self._gmsg("%s %s  Points: %d" % (self._cluelabels[self._clue_number],
                       self._answer.current_clue(), self._current_points))
            self._clue_number += 1
        # we must be somewhere in between
        elif self._clue_number < 4:
            self._current_points = self._points[self._clue_number]
            self._gmsg('%s %s  Points: %d' % (self._cluelabels[self._clue_number],
                       self._answer.give_clue(), self._current_points))
            self._clue_number += 1
        # no one must have gotten it.
        else:
            self._gmsg('No one got it. The answer was: %s' %
                       self._answer.answer)
            self._clue_number = 0
            self._no_plays += 1
            # Stop gameplay after 10 questions of no activity
            if (self._no_plays == 10):
                self._gmsg('It appears I am talking to myself now!')
                self._stop()
            else:
                self._get_new_question()

    def irc_RPL_NAMREPLY(self, *nargs):
        '''
        Called when we get a reply to NAMES
        Using this for tracking user modes, in a simplistic manner
        '''
        if (nargs[1][2] != self._game_channel): return
        users = nargs[1][3].split()
        for u in users:
            split = re.split('(\~|\&|\@|\%|\+)', u)
            try:
                mode = split[1]
                user = split[2]
            except IndexError:
                mode = ''
                user = split[0]
            mode = mode.replace('+', 'voice')
            mode = mode.replace('%', 'halfop')
            mode = mode.replace('@', 'op')
            mode = mode.replace('&', 'admin')
            mode = mode.replace('~', 'owner')
            # This is for us joining the channel and re-checking after mode changes
            try:
                self._userlist[user]
                self._userlist['modes'] = (mode,)
            except:
                self._userlist[user] = {}
                self._userlist[user]['wins'] = 0
                self._userlist[user]['modes'] = (mode,)
                self._userlist[user]['strikes'] = 0

    def signedOn(self):
        '''
        Actions to perform on signon to the server.
        '''
        try:
            config.IDENT_PASS
            self.msg('NickServ', 'identify %s' % config.IDENT_PASS)
        except:
            pass
        self.mode(self.nickname, True, config.DEFAULT_MODES)
        print("Signed on as %s." % (self.nickname,))
        self.join(self._game_channel)
        if self.factory.running:
            self._start(None, None, None)
        else:
            self._gmsg('Welcome to %s!' % self._game_channel)
            self._gmsg("For how to use this bot, just say ?help or '%s help'." % self.nickname)

    def joined(self, channel):
        '''
        Callback runs when the bot joins a channel
        A join automatically receives a NAMES reply, for user listing
        '''
        print("Joined %s." % (channel,))
        if (channel != self._game_channel):
            self.leave(channel, 'No!')
            return

    def kickedFrom(self, channel, kicker, message):
        '''
        If we get kicked from gthe game channel,
        attempt to rejoin.
        '''
        print("Kicked from %s by %s: %s" % (channel, kicker, message))
        if (channel != self._game_channel):
            return
        self.join(self._game_channel)

    def userJoined(self, user, channel):
        '''
        Callback for when other users join the channel
        '''
        if channel != self._game_channel: return
        # Add user to userlist, track wins, modes, and strikes of user
        self._userlist[user] = {}
        self._userlist[user]['wins'] = 0
        self._userlist[user]['modes'] = ('',)
        self._userlist[user]['strikes'] = 0
        # If admin, don't send intro notice and op them
        try:
            self._admins.index(user)
            self.mode(channel, True, 'o', user=user)
            self._userlist[user]['modes'].append('op')
        except:
            self.notice(user, "Welcome to %s!" % self._game_channel)
            self.notice(user, "For how to use this bot, just say ?help or '%s help'." % self.nickname)
            if not self.factory.running:
                self.notice(user, "Just say ?start to start the game when you are ready.")

    def userLeft(self, user, channel):
        '''
        Called when a user leaves the game channel
        '''
        if channel != self._game_channel: return
        if user in self._userlist:
            del self._userlist[user]

    def userQuit(self, user, quitMessage):
        '''
        Called when a user quits
        '''
        if channel != self._game_channel: return
        if user in self._userlist:
            del self._userlist[user]

    def userKicked(self, kickee, channel, kicker, message):
        '''
        Called when a user is kicked from the game channel
        '''
        if channel != self._game_channel: return
        if kickee in self._userlist:
            del self._userlist[kickee]

    def userRenamed(self, oldname, newname):
        '''
        Called when a user changes nicknames
        '''
        if oldname in self._userlist:
            self._userlist[newname] = self._userlist.pop(oldname)

    def modeChanged(self, user, channel, set, modes, args):
        '''
        Called when a mode change is seen
        '''
        if channel != self._game_channel: return
        #print('MODE: %s : direction %d : %s and %s' % (user, set, modes, args))
        # Check if 'our' users are part of a mode change, re-run NAMES
        user_change = False
        for u in self._userlist:
            if (u in args):
                user_change = True
                break
        if (user_change == False): return
        self.sendLine('NAMES %s' % channel)

    def privmsg(self, user, channel, msg):
        '''
        Parses out each message and initiates doing the right thing
        with it.
        '''
        user, temp = user.split('!')
        #print(user+" : "+channel+" : "+msg)
        # ignore STATUSMSGs, lazy check
        if (not channel[0] == "#"):
            return
        # need to strip off colors if present.
        try:
            while not msg[0].isalnum() and not msg[0] == '?':
                msg = msg[1:]
        except IndexError:
            return

        # parses each incoming line, and sees if it's a command for the bot.
        try:
            if (msg[0] == "?"):
                command = msg.replace('?', '').split()[0]
                args = msg.replace('?', '').split()[1:]
                self.select_command(command, args, user, channel)
                return
            elif (msg.split()[0].find(self.nickname) == 0):
                command = msg.split()[1]
                args = msg.replace(self.nickname, '').split()[2:]
                self.select_command(command, args, user, channel)
                return
            # if not, try to match the message to the answer.
            else:
                if msg.lower().strip() == self._answer.answer.lower():
                    self._no_plays = 0
                    self._winner(user, channel)
                    self._save_game()
        except:
            return
        # Assuming this is gameplay
        self._no_plays = 0

    def _winner(self, user, channel):
        '''
        Congratulates the winner for guessing correctly and assigns
        points appropriately, then signals that it was guessed.
        '''
        if channel != self._game_channel:
            self.msg(channel,
                     "I'm sorry, answers must be given in the game channel.")
            return
        self._gmsg("%s GOT IT!" % user)
        try:
            self._scores[user] += self._current_points
        except:
            self._scores[user] = self._current_points
        self._gmsg("%s points have been added to your score!" %
                   str(self._current_points))
        self._clue_number = 0
        self._get_new_question()
        self._userlist[user]['wins'] += 1
        if (self._userlist[user]['wins'] == 2):
            self.mode(channel, True, 'v', user=user)
            self._gmsg('Five correct answers! That earns you a voice!')
            self._userlist[user]['modes'].append('voice')
        elif (self._userlist[user]['wins'] == 4):
            self.mode(channel, True, 'h', user=user)
            self._gmsg('Another fifteen correct answers, have some halfops!')
            self._userlist[user]['modes'].append('halfop')

    def ctcpQuery(self, user, channel, msg):
        '''
        Responds to ctcp requests.
        '''
        msg = str(msg[0][0]).lower()
        user = (user.split("!"))[0]
        if (msg == 'action'): return
        print("CTCP from %s : %s" % (user, msg))
        if (msg == 'version'):
            self.notice(user, "CTCP VERSION: Trivia Bot!")
        elif (msg == 'time'):
            self.notice(user, "CTCP TIME: Trivia Time!")
        elif (msg == 'ping'):
            self.notice(user, "CTCP PING: Trivia Pong!")
        else:
            self.notice(user, "Unknown CTCP Query!")

    def _help(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Replies differently if you are an admin or a regular user.
        Only responds to the user since there could be a game in
        progress.
        '''
        try:
            self._admins.index(user)
        except:
            self.notice(user, "Commands: start, stop, score, standings, "
                       "question, clue, help, next, source")
            return
        self.notice(user, "Commands: start, stop, score, standings, "
                   "question, clue, help, next, source")
        self.notice(user, "Admin commands: skip, restart, die, "
                   "set <user> <score>, save")

    def _show_source(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Only responds to the user since there could be a game in
        progress.
        '''
        self.notice(user, 'My source can be found at: '
                   'https://github.com/genius3000/triviabot')
        self.notice(user, 'Original source can be found at: '
                   'https://github.com/rawsonj/triviabot')

    def select_command(self, command, args, user, channel):
        '''
        Callback that responds to commands given to the bot.

        Need to differentiate between priviledged users and regular
        users.
        '''
        # set up command dicts.
        unpriviledged_commands = {'score': self._score,
                                  'help': self._help,
                                  'start': self._start,
                                  'stop': self._stop,
                                  'source': self._show_source,
                                  'standings': self._standings,
                                  'question': self._show_question,
                                  'clue': self._give_clue,
                                  'next': self._next_vote,
                                  }
        priviledged_commands = {'skip': self._next_question,
                                'restart': self._restart,
                                'die': self._die,
                                'set': self._set_user_score,
                                'save': self._save_game,
                                }
        print(command, args, user, channel)
        try:
            self._admins.index(user)
            is_admin = True
        except:
            is_admin = False
        command = command.lower()
        # the following takes care of sorting out functions and
        # priviledges.
        if not is_admin and command in priviledged_commands.keys():
            self.msg(channel, "%s: You don't tell me what to do." % user)
            self._userlist[user]['strikes'] += 1
            if (self._userlist[user]['strikes'] == 5):
                self.kick(channel, user, "You've earned five strikes, be gone!")
            elif ('halfop' in self._userlist[user]['modes']):
                self.mode(channel, False, 'h', user=user)
                self._userlist[user]['modes'].remove('halfop')
            elif ('voice' in self._userlist[user]['modes']):
                self.mode(channel, False, 'v', user=user)
                self._userlist[user]['modes'].remove('voice')
            return
        elif is_admin and command in priviledged_commands.keys():
            priviledged_commands[command](args, user, channel)
        elif command in unpriviledged_commands.keys():
            unpriviledged_commands[command](args, user, channel)
        else:
            self.describe(channel, 'looks at %s oddly.' % user)

    def _next_vote(self, args, user, channel):
        '''Implements user voting for the next question.

        Need to keep track of who voted, and how many votes.

        '''
        if not self._lc.running:
            self._gmsg("We aren't playing right now.")
            return
        try:
            self._voters.index(user)
            self._gmsg("You already voted, %s, give someone else a chance to "
                       "hate this question" % user)
            return
        except:
            if self._votes < 2:
                self._votes += 1
                self._voters.append(user)
                print(self._voters)
                self._gmsg("%s, you have voted. %s more votes needed to "
                           "skip." % (user, str(3-self._votes)))
            else:
                self._votes = 0
                self._voters = []
                self._next_question(None, None, None)

    def _start(self, args, user, channel):
        '''
        Starts the trivia game.

        TODO: Load scores from last game, if any.
        '''
        if self._lc.running:
            return
        else:
            self._get_new_question()
            self._clue_number = 0
            self._no_plays = 0
            self._lc.start(config.WAIT_INTERVAL)
            self.factory.running = True

    def _stop(self, *args):
        '''
        Stops the game and thanks people for playing,
        then saves the scores.
        '''
        if not self._lc.running:
            return
        else:
            self._lc.stop()
            self._gmsg('Thanks for playing trivia!')
            self._gmsg('Current rankings are:')
            self._standings(None, None, self._game_channel)
            self._gmsg('Scores have been saved, and see you next game!')
            self._save_game()
            self.factory.running = False

    def _save_game(self, *args):
        '''
        Saves the game to the data directory.
        '''
        if not path.exists(config.SAVE_DIR):
            makedirs(config.SAVE_DIR)
        with open(config.SAVE_DIR+'scores.json', 'w') as savefile:
            json.dump(self._scores, savefile)
            print("Scores have been saved.")

    def _load_game(self):
        '''
        Loads the running data from previous games.
        '''
        # ensure initialization
        self._scores = {}
        if not path.exists(config.SAVE_DIR):
            print("Save directory doesn't exist.")
            return
        try:
            with open(config.SAVE_DIR+'scores.json', 'r') as savefile:
                temp_dict = json.load(savefile)
        except:
            print("Save file doesn't exist.")
            return
        for name in temp_dict.keys():
            self._scores[str(name)] = int(temp_dict[name])
        print(self._scores)
        print("Scores loaded.")

    def _set_user_score(self, args, user, channel):
        '''
        Administrative action taken to adjust scores, if needed.
        '''
        try:
            self._scores[args[0]] = int(args[1])
        except:
            self.notice(user, args[0]+" not in scores database.")
            return
        self.notice(user, args[0]+" score set to "+args[1])

    def _restart(self, *args):
        '''
        Restart the bot.
        '''
        self._restarting = True
        self.quit('Restarting eh')

    def _die(self, *args):
        '''
        Terminates execution of the bot.
        '''
        self._quit = True
        self.quit(config.DEFAULT_QUIT)

    def connectionLost(self, reason):
        '''
        Called when connection is lost
        '''
        global reactor
        if self._restarting:
            execl(sys.executable, *([sys.executable]+sys.argv))
        elif self._quit:
            reactor.stop()

    def _score(self, args, user, channel):
        '''
        Tells the user their score.
        '''
        try:
            self.notice(user, "Your current score is: %s" %
                       str(self._scores[user]))
        except:
            self.notice(user, "You aren't in my database.")

    def _next_question(self, args, user, channel):
        '''
        Administratively skips the current question.
        '''
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        self._gmsg("Question has been skipped. The answer was: %s" %
                   self._answer.answer)
        self._clue_number = 0
        self._lc.stop()
        self._lc.start(config.WAIT_INTERVAL)

    def _standings(self, args, user, channel):
        '''
        Tells the user the complete standings in the game.
        '''
        if channel == self.nickname:
            dst = user
        else:
            if channel != self._game_channel: return
            dst = channel
        score_list = []
        if not user is None:
            self.notice(dst, "The current trivia standings are: ")
        sorted_scores = sorted(self._scores.iteritems(), key=lambda x:x[1], reverse=True)
        for rank, (player, score) in enumerate(sorted_scores, start=1):
            formatted_score = "#%s: %s with %s points" % (rank, player, score)
            score_list.append(formatted_score)
        # Will have to split this at a certain length later
        self.notice(dst, ", ".join([str(player) for player in score_list]))

    def _show_question(self, args, user, channel):
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        self._gmsg("Current question: %s" % self._question)

    def _give_clue(self, args, user, channel):
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        # Just stop and start gameplay timer. It will give a new clue
        # and wait another 'WAIT_INTERVAL' until the next clue
        self._lc.stop()
        self._lc.start(config.WAIT_INTERVAL)

    def _get_new_question(self):
        '''
        Selects a new question from the questions directory and
        sets it.
        '''
        damaged_question = True
        while damaged_question:
            # randomly select file
            filename = choice(listdir(self._questions_dir))
            fd = open(config.Q_DIR+filename)
            lines = fd.read().splitlines()
            myline = choice(lines)
            fd.close()
            try:
                self._question, temp_answer = myline.split('`')
            except ValueError:
                print("Broken question:")
                print(myline)
                continue
            self._answer.set_answer(temp_answer.strip())
            damaged_question = False
Example #20
0
 def make_index_or_area_overall_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                    chain: TranslationChain, result: Result):
     if qt == 'index_overall':
         gen = [result['index']]
         tag = '指标'
     else:
         gen = [result['area'], result['index']]
         tag = ''
     data = self._search_double_direct_then_feed(chain)
     builder.feed_data(data)
     for x, y, f, n in builder.product_data_with_feed(
             *gen,
             if_x_is_none=lambda _1, _2, _3, na: f'无{na.subject()}的数据记录,无法比较',
             if_y_is_none=lambda _1, _2, _3, na: f'无{na.subject()}的父级{tag}数据记录,无法比较'
     ):
         f.life_check(result['year'][0])
         if not f:
             answer.add_answer(f'无{n.subject()}父级{tag}数据记录,无法比较')
             return
         answer.begin_sub_answers()
         unit_x, unit_y = x.unit, y[0].unit
         if qt == 'area_overall':  # 交换值域
             f.area, f.name = f.name, n.name
         n.repr = f.repr = x.repr
         answer.add_sub_answers(f'{n.subject()}为{x.val()}')
         answer.add_sub_answers(f'其父级{tag}{f.subject()}为{y[0].val()}')
         if unit_x != unit_y:
             answer.add_sub_answers('两者单位不同,无法比较')
             answer.end_sub_answers()
             return
         res1 = builder.binary_calculation(x.value, y[0].value, truediv, percentage=True)
         if builder.add_if_is_not_none(res1, no=f'无效的{n.subject()}值类型,无法比较'):
             answer.add_sub_answers(f'前者占后者的{res1}%')
         res2 = builder.binary_calculation(y[0].value, x.value, truediv)
         if builder.add_if_is_not_none(res2, no=f'无效的{n.subject()}值类型,无法比较'):
             answer.add_sub_answers(f'后者是前者的{res2}倍')
         answer.end_sub_answers()
Example #21
0
 def make_indexes_or_areas_2m_or_2n_compare_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                                chain: TranslationChain, result: Result):
     # set operator
     if qt in ('areas_2m_compare', 'indexes_2m_compare'):
         operator = truediv
     else:
         operator = sub
     # set gen and flatten
     if qt in ('areas_2m_compare', 'areas_2n_compare'):
         gen = [result['area'], result['index']]
         unpack = True
     else:
         gen = [result['index']]
         unpack = False
     # code begin
     data = self._search_direct(chain, unpack=unpack)
     builder.feed_data(data)
     for item, name in builder.product_data_with_name(*gen):
         x, y = item
         if builder.binary_decision(
                 x, y,
                 not_x=f'无关于{result["year"][0]}年的{name.subject()}的记录',
                 not_y=f'无关于{result["year"][1]}年的{name.subject()}的记录'
         ):
             answer.begin_sub_answers()
             res = builder.binary_calculation(x.value, y.value, operator)
             if builder.add_if_is_not_none(res, no=f'{name.subject()}的记录为无效的值类型,无法比较'):
                 answer.add_sub_answers(f'{result["year"][0]}年的{name.subject()}为{x.val()}')
                 answer.add_sub_answers(f'{result["year"][1]}年的{name.subject()}为{y.val()}')
                 if qt in ('areas_2m_compare', 'indexes_2m_compare'):
                     ux, uy = x.unit, y.unit
                     # 单位为%的数值不支持此类型比较
                     if ux == uy == '%':
                         answer.add_sub_answers(f'它们单位为‘%’,不支持此类型的比较')
                     else:
                         answer.add_sub_answers(f'前者是后者的{res}倍')
                 else:
                     answer.add_sub_answers(f'前者比后者{sign(res, ("减少", "增加"))}{abs(res)}{x.unit}')
             answer.end_sub_answers()
Example #22
0
class triviabot(irc.IRCClient):
    '''
    This is the irc bot portion of the trivia bot.

    It implements the whole program so is kinda big. The algorithm is
    implemented by a series of callbacks, initiated by an admin on the
    server.
    '''
    def __init__(self):
        self._answer = Answer()
        self._question = ''
        self._scores = {}
        self._clue_number = 0
        self._admins = list(config.ADMINS)
        self._game_channel = config.GAME_CHANNEL
        self._current_points = 5
        self._questions_dir = config.Q_DIR
        self._lc = LoopingCall(self._play_game)
        self._load_game()
        self._votes = 0
        self._voters = []

    def _get_nickname(self):
        return self.factory.nickname

    nickname = property(_get_nickname)

    def _get_lineRate(self):
        return self.factory.lineRate

    lineRate = property(_get_lineRate)

    def _cmsg(self, dest, msg):
        """
        Write a colorized message.
        """

        self.msg(dest, "%s%s" % (config.COLOR_CODE, msg))

    def _gmsg(self, msg):
        """
        Write a message to the channel playing the trivia game.
        """

        self._cmsg(self._game_channel, msg)

    def _play_game(self):
        '''
        Implements the main loop of the game.
        '''
        points = {0: 5,
                  1: 3,
                  2: 2,
                  3: 1
                  }
        if self._clue_number == 0:
            self._votes = 0
            self._voters = []
            self._get_new_question()
            self._current_points = points[self._clue_number]
            # Blank line.
            self._gmsg("")
            self._gmsg("Next question:")
            self._gmsg(self._question)
            self._gmsg("Clue: %s" % self._answer.current_clue())
            self._clue_number += 1
        # we must be somewhere in between
        elif self._clue_number < 4:
            self._current_points = points[self._clue_number]
            self._gmsg("Question:")
            self._gmsg(self._question)
            self._gmsg('Clue: %s' % self._answer.give_clue())
            self._clue_number += 1
        # no one must have gotten it.
        else:
            self._gmsg('No one got it. The answer was: %s' %
                       self._answer.answer)
            self._clue_number = 0
            self._get_new_question()
            # self._lc.reset()

    def signedOn(self):
        '''
        Actions to perform on signon to the server.
        '''
        self.join(self._game_channel)
        self.msg('NickServ', 'identify %s' % config.IDENT_STRING)
        print("Signed on as %s." % (self.nickname,))
        if self.factory.running:
            self._start(None, None, None)
        else:
            self._gmsg('Welcome to %s!' % self._game_channel)
            self._gmsg("Have an admin start the game when you are ready.")
            self._gmsg("For how to use this bot, just say ?help or")
            self._gmsg("%s help." % self.nickname)

    def joined(self, channel):
        '''
        Callback runs when the bot joins a channel
        '''
        print("Joined %s." % (channel,))

    def privmsg(self, user, channel, msg):
        '''
        Parses out each message and initiates doing the right thing
        with it.
        '''
        user, temp = user.split('!')
        print(user+" : "+channel+" : "+msg)
        # need to strip off colors if present.
        try:
            while not msg[0].isalnum() and not msg[0] == '?':
                msg = msg[1:]
        except IndexError:
            return

        # parses each incoming line, and sees if it's a command for the bot.
        try:
            if (msg[0] == "?"):
                command = msg.replace('?', '').split()[0]
                args = msg.replace('?', '').split()[1:]
                self.select_command(command, args, user, channel)
                return
            elif (msg.split()[0].find(self.nickname) == 0):
                command = msg.split()[1]
                args = msg.replace(self.nickname, '').split()[2:]
                self.select_command(command, args, user, channel)
                return
            # if not, try to match the message to the answer.
            else:
                if msg.lower().strip() == self._answer.answer.lower():
                    self._winner(user, channel)
                    self._save_game()
        except:
            return

    def _winner(self, user, channel):
        '''
        Congratulates the winner for guessing correctly and assigns
        points appropriately, then signals that it was guessed.
        '''
        if channel != self._game_channel:
            self.msg(channel,
                     "I'm sorry, answers must be given in the game channel.")
            return
        self._gmsg("%s GOT IT!" % user.upper())
        try:
            self._scores[user] += self._current_points
        except:
            self._scores[user] = self._current_points
        if self._current_points == 1:
            self._gmsg("%s point has been added to your score!" %
                       str(self._current_points))
        else:
            self._gmsg("%s points have been added to your score!" %
                       str(self._current_points))
        self._clue_number = 0
        self._get_new_question()

    def ctcpQuery(self, user, channel, msg):
        '''
        Responds to ctcp requests.
        Currently just reports them.
        '''
        print("CTCP recieved: "+user+":"+channel+": "+msg[0][0]+" "+msg[0][1])

    def _help(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Replies differently if you are an admin or a regular user.
        Only responds to the user since there could be a game in
        progress.
        '''
        try:
            self._admins.index(user)
        except:
            self._cmsg(user, "I'm nameless's trivia bot.")
            self._cmsg(user, "Commands: score, standings, giveclue, help, "
                       "next, source")
            return
        self._cmsg(user, "I'm nameless's trivia bot.")
        self._cmsg(user, "Commands: score, standings, giveclue, help, next, "
                   "skip, source")
        self._cmsg("Admin commands: die, set <user> <score>, start, stop, "
                   "save")

    def _show_source(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Only responds to the user since there could be a game in
        progress.
        '''
        self._cmsg(user, 'My source can be found at: '
                   'https://github.com/rawsonj/triviabot')

    def select_command(self, command, args, user, channel):
        '''
        Callback that responds to commands given to the bot.

        Need to differentiate between priviledged users and regular
        users.
        '''
        # set up command dicts.
        unpriviledged_commands = {'score': self._score,
                                  'help': self._help,
                                  'source': self._show_source,
                                  'standings': self._standings,
                                  'giveclue': self._give_clue,
                                  'next': self._next_vote,
                                  'skip': self._next_question
                                  }
        priviledged_commands = {'die': self._die,
                                'set': self._set_user_score,
                                'start': self._start,
                                'stop': self._stop,
                                'save': self._save_game,
                                }
        print(command, args, user, channel)
        try:
            self._admins.index(user)
            is_admin = True
        except:
            is_admin = False

        # the following takes care of sorting out functions and
        # priviledges.
        if not is_admin and command in priviledged_commands.keys():
            self.msg(channel, "%s: You don't tell me what to do." % user)
            return
        elif is_admin and command in priviledged_commands.keys():
            priviledged_commands[command](args, user, channel)
        elif command in unpriviledged_commands.keys():
            unpriviledged_commands[command](args, user, channel)
        else:
            self.describe(channel, '%slooks at %s oddly.' %
                          (config.COLOR_CODE, user))

    def _next_vote(self, args, user, channel):
        '''Implements user voting for the next question.

        Need to keep track of who voted, and how many votes.

        '''
        if not self._lc.running:
            self._gmsg("We aren't playing right now.")
            return
        try:
            self._voters.index(user)
            self._gmsg("You already voted, %s, give someone else a chance to "
                       "hate this question" % user)
            return
        except:
            if self._votes < 2:
                self._votes += 1
                self._voters.append(user)
                print(self._voters)
                self._gmsg("%s, you have voted. %s more votes needed to "
                           "skip." % (user, str(3-self._votes)))
            else:
                self._votes = 0
                self._voters = []
                self._next_question(None, None, None)

    def _start(self, args, user, channel):
        '''
        Starts the trivia game.

        TODO: Load scores from last game, if any.
        '''
        if self._lc.running:
            return
        else:
            self._lc.start(config.WAIT_INTERVAL)
            self.factory.running = True

    def _stop(self, *args):
        '''
        Stops the game and thanks people for playing,
        then saves the scores.
        '''
        if not self._lc.running:
            return
        else:
            self._lc.stop()
            self._gmsg('Thanks for playing trivia!')
            self._gmsg('Current rankings were:')
            self._standings(None, self._game_channel, None)
            self._gmsg('''Scores have been saved, and see you next game!''')
            self._save_game()
            self.factory.running = False

    def _save_game(self, *args):
        '''
        Saves the game to the data directory.
        '''
        if not path.exists(config.SAVE_DIR):
            makedirs(config.SAVE_DIR)
        with open(config.SAVE_DIR+'scores.json', 'w') as savefile:
            json.dump(self._scores, savefile)
            print("Scores have been saved.")

    def _load_game(self):
        '''
        Loads the running data from previous games.
        '''
        # ensure initialization
        self._scores = {}
        if not path.exists(config.SAVE_DIR):
            print("Save directory doesn't exist.")
            return
        try:
            with open(config.SAVE_DIR+'scores.json', 'r') as savefile:
                temp_dict = json.load(savefile)
        except:
            print("Save file doesn't exist.")
            return
        for name in temp_dict.keys():
            self._scores[str(name)] = int(temp_dict[name])
        print(self._scores)
        print("Scores loaded.")

    def _set_user_score(self, args, user, channel):
        '''
        Administrative action taken to adjust scores, if needed.
        '''
        try:
            self._scores[args[0]] = int(args[1])
        except:
            self._cmsg(user, args[0]+" not in scores database.")
            return
        self._cmsg(user, args[0]+" score set to "+args[1])

    def _die(self, *args):
        '''
        Terminates execution of the bot.
        Need to dig into twisted to figure out how this happens.
        '''
        global reactor
        self.quit(message='This is triviabot, signing off.')
        reactor.stop()
        # figure out how to kill the bot

    def _score(self, args, user, channel):
        '''
        Tells the user their score.
        '''
        try:
            self._cmsg(user, "Your current score is: %s" %
                       str(self._scores[user]))
        except:
            self._cmsg(user, "You aren't in my database.")

    def _next_question(self, args, user, channel):
        '''
        Administratively skips the current question.
        '''
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        self._gmsg("Question has been skipped. The answer was: %s" %
                   self._answer.answer)
        self._clue_number = 0
        self._lc.stop()
        self._lc.start(config.WAIT_INTERVAL)

    def _standings(self, args, user, channel):
        '''
        Tells the user the complete standings in the game.

        TODO: order them.
        '''
        self._cmsg(user, "The current trivia standings are: ")
        sorted_scores = sorted(self._scores.iteritems(), key=lambda k, v:
                               (v, k), reverse=True)
        for rank, (player, score) in enumerate(sorted_scores, start=1):
            formatted_score = "%s: %s: %s" % (rank, player, score)
            self._cmsg(user, str(formatted_score))

    def _give_clue(self, args, user, channel):
        if not self._lc.running:
            self._gmsg("we are not playing right now.")
            return
        self._cmsg(channel, "Question: ")
        self._cmsg(channel, self._question)
        self._cmsg(channel, "Clue: "+self._answer.current_clue())

    def _get_new_question(self):
        '''
        Selects a new question from the questions directory and
        sets it.
        '''
        damaged_question = True
        while damaged_question:
            # randomly select file
            filename = choice(listdir(self._questions_dir))
            fd = open(config.Q_DIR+filename)
            lines = fd.read().splitlines()
            myline = choice(lines)
            fd.close()
            try:
                self._question, temp_answer = myline.split('`')
            except ValueError:
                print("Broken question:")
                print(myline)
                continue
            self._answer.set_answer(temp_answer.strip())
            damaged_question = False
Example #23
0
    def organize(self, qt: str, chain: TranslationChain, result: Result) -> Answer:
        answer = Answer()
        builder = AnswerBuilder(answer)
        # 年度总体状况
        if qt == 'year_status':
            self.make_year_status_ans(answer, chain, result)
        # 年度目录状况
        elif qt == 'catalog_status':
            self.make_catalog_status_ans(answer, builder, chain, result)
        # 年度目录包含哪些
        elif qt == 'exist_catalog':
            self.make_exist_catalog_ans(answer, chain, result)
        # 指标值
        elif qt == 'index_value':
            self.make_index_value_ans(answer, builder, chain, result)
        # 指标占总比 & 地区指标占总比
        elif qt in ('index_overall', 'area_overall'):
            self.make_index_or_area_overall_ans(qt, answer, builder, chain, result)
        # 指标组成
        elif qt == 'index_compose':
            self.make_index_compose_ans(answer, builder, chain, result)
        # 指标倍数比较(只有两个指标) & 指标数量比较(只有两个指标)
        elif qt in ('indexes_m_compare', 'indexes_n_compare'):
            self.make_indexes_m_or_n_compare_ans(qt, answer, builder, chain, result)
        elif qt in ('indexes_2m_compare', 'indexes_2n_compare', 'areas_2m_compare', 'areas_2n_compare'):
            self.make_indexes_or_areas_2m_or_2n_compare_ans(qt, answer, builder, chain, result)
        # 指标值同比比较
        elif qt == 'indexes_g_compare':
            self.make_indexes_g_compare_ans(answer, builder, chain, result)
        # 地区指标值
        elif qt == 'area_value':
            self.make_area_value_ans(answer, builder, chain, result)
        # 地区指标占总比的变化 & 指标占总比的变化
        elif qt in ('area_2_overall', 'index_2_overall'):
            self.make_index_or_area_2_overall_ans(qt, answer, builder, chain, result)
        # 地区指标倍数比较(只有两个地区) & 地区指标数量比较(只有两个地区)
        elif qt in ('areas_m_compare', 'areas_n_compare'):
            self.make_areas_m_or_n_compare_ans(qt, answer, builder, chain, result)
        # 地区指标值同比比较
        elif qt == 'areas_g_compare':
            self.make_areas_g_compare_ans(answer, builder, chain, result)
        # 两年目录的变化 & 两年指标的变化
        elif qt in ('catalog_change', 'index_change'):
            self.make_catalog_or_index_change_ans(qt, answer, builder, chain, result)
        # 多年目录的变化 & 多年指标的变化
        elif qt in ('catalogs_change', 'indexes_change'):
            self.make_catalogs_or_indexes_change_ans(qt, answer, chain, result)
        # 指标值变化(多年份)
        elif qt in ('indexes_trend', 'areas_trend'):
            self.make_indexes_or_areas_trend_ans(qt, answer, builder, chain, result)
        # 占总指标比的变化
        elif qt in ('indexes_overall_trend', 'areas_overall_trend'):
            self.make_indexes_or_areas_overall_trend_ans(qt, answer, builder, chain, result)
        # 几个年份中的最值
        elif qt in ('indexes_max', 'areas_max'):
            self.make_indexes_or_areas_max_ans(qt, answer, builder, chain, result)
        # 何时开始统计此指标
        elif qt == 'begin_stats':
            self.make_begin_stats_ans(answer, builder, chain, result)

        return answer
Example #24
0
 def make_index_or_area_2_overall_ans(self, qt: str, answer: Answer, builder: AnswerBuilder,
                                      chain: TranslationChain, result: Result):
     years = '、'.join(result['year'])
     # init
     if qt == 'area_2_overall':
         unpack = True
         gen = [result["area"], result["index"]]
     else:
         unpack = False
         gen = [result["index"]]
     # collect data
     data = self._search_double_direct_then_feed(chain, unpack=unpack)
     builder.feed_data(data)
     # product data
     for x, y, f, n in builder.product_data_with_feed(
             *gen,
             if_x_is_none=lambda _1, _2, _3, na: f'无{years}这几年{na.subject()}的数据记录,无法比较',
             if_y_is_none=lambda _1, _2, _3, na: f'无{years}这几年{na.subject()}的父级数据记录,无法比较'
     ):
         temp = []  # 记录两次计算的结果值
         for i, year in enumerate(result["year"]):
             f.life_check(year)
             if not f:
                 answer.add_answer(f'无{year}年{n.subject()}的父级数据记录,无法比较')
                 continue
             unit_x, unit_y = x[i].unit, y[i].unit
             answer.begin_sub_answers()
             n.repr = x[i].repr
             answer.add_sub_answers(f'{year}年{n.subject()}为{x[i].value}{unit_x}')
             answer.add_sub_answers(f'其总体{f.name}{y[i].repr}为{y[i].value}{unit_y}')
             if unit_x != unit_y:
                 answer.add_sub_answers('两者单位不同,无法比较')
                 answer.end_sub_answers()
                 continue
             res = builder.binary_calculation(x[i].value, y[i].value, truediv, percentage=True)
             if builder.add_if_is_not_none(res, no=f'无效的{n}值类型,无法比较'):
                 answer.add_sub_answers(f'约占总体的{res}%')
                 temp.append(res)
             answer.end_sub_answers()
         if len(temp) == 2:
             num = round(temp[0] - temp[1], 2)
             answer.add_answer(f'前者相比后者{sign(num, ("降低", "提高"))}{abs(num)}%')
Example #25
0
class triviabot(irc.IRCClient):
    '''
    This is the irc bot portion of the trivia bot.

    It implements the whole program so is kinda big. The algorithm is
    implemented by a series of callbacks, initiated by an admin on the
    server.
    '''

    def __init__(self):
        self._answer = Answer()
        self._question = ''
        self._scores = {}
        self._clue_number = 0
        self._admins = list(config.ADMINS)
        self._admins.append(config.OWNER)
        self._game_channel = config.GAME_CHANNEL
        self._current_points = 5
        self._questions_dir = config.Q_DIR
        self._lc = LoopingCall(self._play_game)
        self._quit = False
        self._restarting = False
        self._load_game()
        self._votes = 0
        self._voters = []

    def _get_nickname(self):
        return self.factory.nickname

    nickname = property(_get_nickname)

    def _get_lineRate(self):
        return self.factory.lineRate

    lineRate = property(_get_lineRate)

    def _cmsg(self, dest, msg):
        """
        Write a colorized message.
        """

        self.msg(dest, "{}{}".format(config.COLOR_CODE, msg))

    def _gmsg(self, msg):
        """
        Write a message to the channel playing the trivia game.
        """

        self._cmsg(self._game_channel, msg)

    def _play_game(self):
        '''
        Implements the main loop of the game.
        '''
        points = {0: 5,
                  1: 3,
                  2: 2,
                  3: 1
                  }
        if self._clue_number == 0:
            self._votes = 0
            self._voters = []
            self._get_new_question()
            self._current_points = points[self._clue_number]
            # Blank line.
            self._gmsg("")
            self._gmsg("Next question:")
            self._gmsg(self._question)
            self._gmsg("Clue: {}".format(self._answer.current_clue()))
            self._clue_number += 1
        # we must be somewhere in between
        elif self._clue_number < 4:
            self._current_points = points[self._clue_number]
            self._gmsg("Question:")
            self._gmsg(self._question)
            self._gmsg("Clue: {}".format(self._answer.give_clue()))
            self._clue_number += 1
        # no one must have gotten it.
        else:
            self._gmsg("No one got it. The answer was: {}"
                       .format(self._answer.answer))
            self._clue_number = 0
            self._get_new_question()
            # self._lc.reset()

    def signedOn(self):
        '''
        Actions to perform on signon to the server.
        '''
        self.join(self._game_channel)
        self.msg("NickServ", "identify {}".format(config.IDENT_STRING))
        print("Signed on as {}.".format(self.nickname))
        if self.factory.running:
            self._start(None, None, None)
        else:
            self._gmsg("Welcome to {}!".format(self._game_channel))
            self._gmsg("Have an admin start the game when you are ready.")
            self._gmsg("For how to use this bot, just say ?help or")
            self._gmsg("{} help.".format(self.nickname))

    def joined(self, channel):
        '''
        Callback runs when the bot joins a channel
        '''
        print("Joined {}.".format((channel)))

    def privmsg(self, user, channel, msg):
        '''
        Parses out each message and initiates doing the right thing
        with it.
        '''
        user, temp = user.split('!')
        print(user + " : " + channel + " : " + msg)
        # need to strip out non-printable characters if present.
        printable = string.printable
        msg = ''.join(filter(lambda x: x in printable, msg))

        # parses each incoming line, and sees if it's a command for the bot.
        try:
            if (msg[0] == "?"):
                command = msg.replace('?', '').split()[0]
                args = msg.replace('?', '').split()[1:]
                self.select_command(command, args, user, channel)
                return
            elif (msg.split()[0].find(self.nickname) == 0):
                command = msg.split()[1]
                args = msg.replace(self.nickname, '').split()[2:]
                self.select_command(command, args, user, channel)
                return
            # if not, try to match the message to the answer.
            else:
                if msg.lower().strip() == self._answer.answer.lower():
                    self._winner(user, channel)
                    self._save_game()
        except Exception as e:
            print(e)
            return

    def _winner(self, user, channel):
        '''
        Congratulates the winner for guessing correctly and assigns
        points appropriately, then signals that it was guessed.
        '''
        if channel != self._game_channel:
            self.msg(channel,
                     "I'm sorry, answers must be given in the game channel.")
            return
        self._gmsg("{} GOT IT!".format(user.upper()))
        self._gmsg("""If there was any doubt, the correct answer was: {}""".format(self._answer.answer))
        try:
            self._scores[user] += self._current_points
        except:
            self._scores[user] = self._current_points
        if self._current_points == 1:
            self._gmsg("{} point has been added to your score!"
                       .format(str(self._current_points)))
        else:
            self._gmsg("{} points have been added to your score!"
                       .format(str(self._current_points)))
        self._clue_number = 0
        self._get_new_question()

    def ctcpQuery(self, user, channel, msg):
        '''
        Responds to ctcp requests.
        Currently just reports them.
        '''
        print("CTCP recieved: " + user + ":" + channel +
              ": " + msg[0][0] + " " + msg[0][1])

    def _help(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Replies differently if you are an admin or a regular user.
        Only responds to the user since there could be a game in
        progress.
        '''
        try:
            self._admins.index(user)
        except:
            self._cmsg(user, "I'm {}'s trivia bot.".format(config.OWNER))
            self._cmsg(user, "Commands: score, standings, giveclue, help, "
                       "next, source")
            return
        self._cmsg(user, "I'm {}'s trivia bot.".format(config.OWNER))
        self._cmsg(user, "Commands: score, standings, giveclue, help, next, "
                   "skip, source")
        self._cmsg(user, "Admin commands: die, set <user> <score>, start, stop, "
                   "save")

    def _show_source(self, args, user, channel):
        '''
        Tells people how to use the bot.
        Only responds to the user since there could be a game in
        progress.
        '''
        self._cmsg(user, 'My source can be found at: '
                   'https://github.com/rawsonj/triviabot')

    def select_command(self, command, args, user, channel):
        '''
        Callback that responds to commands given to the bot.

        Need to differentiate between priviledged users and regular
        users.
        '''
        # set up command dicts.
        unpriviledged_commands = {'score': self._score,
                                  'help': self._help,
                                  'source': self._show_source,
                                  'standings': self._standings,
                                  'giveclue': self._give_clue,
                                  'next': self._next_vote,
                                  'skip': self._next_question
                                  }
        priviledged_commands = {'die': self._die,
                                'restart': self._restart,
                                'set': self._set_user_score,
                                'start': self._start,
                                'stop': self._stop,
                                'save': self._save_game,
                                }
        print(command, args, user, channel)
        try:
            self._admins.index(user)
            is_admin = True
        except:
            is_admin = False

        # the following takes care of sorting out functions and
        # priviledges.
        if not is_admin and command in priviledged_commands:
            self.msg(channel, "{}: You don't tell me what to do."
                     .format(user))
            return
        elif is_admin and command in priviledged_commands:
            priviledged_commands[command](args, user, channel)
        elif command in unpriviledged_commands:
            unpriviledged_commands[command](args, user, channel)
        else:
            self.describe(channel, "{}looks at {} oddly."
                          .format(config.COLOR_CODE, user))

    def _next_vote(self, args, user, channel):
        '''Implements user voting for the next question.

        Need to keep track of who voted, and how many votes.

        '''
        if not self._lc.running:
            self._gmsg("We aren't playing right now.")
            return
        try:
            self._voters.index(user)
            self._gmsg("You already voted, {}, give someone else a chance to "
                       "hate this question".format(user))
            return
        except:
            if self._votes < 2:
                self._votes += 1
                self._voters.append(user)
                print(self._voters)
                self._gmsg("{}, you have voted. {} more votes needed to "
                           "skip.".format(user, str(3 - self._votes)))
            else:
                self._votes = 0
                self._voters = []
                self._next_question(None, None, None)

    def _start(self, args, user, channel):
        '''
        Starts the trivia game.

        TODO: Load scores from last game, if any.
        '''
        if self._lc.running:
            return
        else:
            self._lc.start(config.WAIT_INTERVAL)
            self.factory.running = True

    def _stop(self, *args):
        '''
        Stops the game and thanks people for playing,
        then saves the scores.
        '''
        if not self._lc.running:
            return
        else:
            self._lc.stop()
            self._gmsg('Thanks for playing trivia!')
            self._gmsg('Current rankings were:')
            self._standings(None, self._game_channel, None)
            self._gmsg('''Scores have been saved, and see you next game!''')
            self._save_game()
            self.factory.running = False

    def _save_game(self, *args):
        '''
        Saves the game to the data directory.
        '''
        with open(os.path.join(config.SAVE_DIR, 'scores.json'), 'w') as savefile:
            json.dump(self._scores, savefile)
            print("Scores have been saved.")

    def _load_game(self):
        '''
        Loads the running data from previous games.
        '''
        # ensure initialization
        self._scores = {}
        if not path.exists(config.SAVE_DIR):
            print("Save directory doesn't exist.")
            return
        try:
            with open(os.path.join(config.SAVE_DIR, 'scores.json'), 'r') as savefile:
                temp_dict = json.load(savefile)
        except:
            print("Save file doesn't exist.")
            return
        for name in temp_dict.keys():
            self._scores[str(name)] = int(temp_dict[name])
        print(self._scores)
        print("Scores loaded.")

    def _set_user_score(self, args, user, channel):
        '''
        Administrative action taken to adjust scores, if needed.
        '''
        try:
            self._scores[args[0]] = int(args[1])
        except:
            self._cmsg(user, args[0] + " not in scores database.")
            return
        self._cmsg(user, args[0] + " score set to " + args[1])

    def _die(self, *args):
        '''
        Terminates execution of the bot.
        '''
        self._quit = True
        self.quit(message='This is triviabot, signing off.')

    def _restart(self, *args):
        '''
        Restarts the bot
        '''
        self._restarting = True
        print('Restarting')
        self.quit(message='Triviabot restarting.')

    def connectionLost(self, reason):
        '''
        Called when connection is lost
        '''
        global reactor
        if self._restarting:
            try:
                execl(sys.executable, *([sys.executable]+sys.argv))
            except Exception as e:
                print("Failed to restart: {}".format(e))
        if self._quit:
            reactor.stop()

    def _score(self, args, user, channel):
        '''
        Tells the user their score.
        '''
        try:
            self._cmsg(user, "Your current score is: {}"
                       .format(str(self._scores[user])))
        except:
            self._cmsg(user, "You aren't in my database.")

    def _next_question(self, args, user, channel):
        '''
        Administratively skips the current question.
        '''
        if not self._lc.running:
            self._gmsg("We are not playing right now.")
            return
        self._gmsg("Question has been skipped. The answer was: {}".format(self._answer.answer))
        self._clue_number = 0
        self._lc.stop()
        self._lc.start(config.WAIT_INTERVAL)

    def _standings(self, args, user, channel):
        '''
        Tells the user the complete standings in the game.

        TODO: order them.
        '''
        self._cmsg(user, "The current trivia standings are: ")
        sorted_scores = sorted(self._scores.iteritems(), key=lambda (k, v): (v, k), reverse=True)
        for rank, (player, score) in enumerate(sorted_scores, start=1):
            formatted_score = "{}: {}: {}".format(rank, player, score)
            self._cmsg(user, formatted_score)

    def _give_clue(self, args, user, channel):
        if not self._lc.running:
            self._gmsg("we are not playing right now.")
            return
        self._cmsg(channel, "Question: ")
        self._cmsg(channel, self._question)
        self._cmsg(channel, "Clue: " + self._answer.current_clue())

    def _get_new_question(self):
        '''
        Selects a new question from the questions directory and
        sets it.
        '''
        damaged_question = True
        while damaged_question:
            # randomly select file
            filename = choice(listdir(self._questions_dir))
            fd = open(os.path.join(config.Q_DIR, filename))
            lines = fd.read().splitlines()
            myline = choice(lines)
            fd.close()
            try:
                self._question, temp_answer = myline.split('`')
            except ValueError:
                print("Broken question:")
                print(myline)
                continue
            self._answer.set_answer(temp_answer.strip())
            damaged_question = False
Example #26
0
 def make_year_status_ans(self, answer: Answer, chain: TranslationChain, result: Result):
     data = self._search_direct(chain)
     answer.add_answer(f'{result["year"][0]}年,{data[0].info}')