def poll_end(bot: DeltaBot, args: str, message: Message, replies: Replies) -> None: """Close the poll with the given id.""" if args: addr = message.get_sender_contact().addr try: with session_scope() as session: poll = session.query(Poll).filter_by(id=int(args[0]), addr=addr).one() text, html = _format_poll(bot, poll, closed=True) addresses = set(vote.addr for vote in poll.votes) session.delete(poll) addresses.add(addr) for addr in addresses: contact = bot.get_contact(addr) if not contact.is_blocked(): replies.add(text=text, html=html, chat=bot.get_chat(contact)) except NoResultFound: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat()) else: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat())
def vote(bot: DeltaBot, args: str, message: Message, replies: Replies) -> None: """Vote in polls.""" if len(args) == 2: option_id = int(args[1]) addr = message.get_sender_contact().addr try: with session_scope() as session: poll = session.query(Poll).filter_by(id=int(args[0])).one() if addr in [v.addr for v in poll.votes]: text, html = "❌ You already voted", None elif option_id not in [o.id for o in poll.options]: text, html = "❌ Invalid option number", None else: poll.votes.append(Vote(addr=addr, value=option_id)) is_admin = addr == poll.addr text, html = _format_poll(bot, poll, voted=True, is_admin=is_admin) replies.add(text=text, html=html, chat=message.get_sender_chat()) except NoResultFound: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat()) else: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat())
def poll_status(bot: DeltaBot, args: str, message: Message, replies: Replies) -> None: """Get poll status.""" addr = message.get_sender_contact().addr if args: try: with session_scope() as session: poll = session.query(Poll).filter_by(id=int(args[0])).one() is_admin = addr == poll.addr voted = is_admin or addr in [v.addr for v in poll.votes] if voted: text, html = _format_poll(bot, poll, voted=True, is_admin=is_admin) else: text = "❌ You can't see poll status until you vote" html = None replies.add(text=text, html=html, chat=message.get_sender_chat()) except NoResultFound: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat()) else: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat())
def ac_incoming_message(self, message: Message) -> None: # we always accept incoming messages to remove the need for # bot authors to having to deal with deaddrop/contact requests. message.create_chat() self.logger.debug( f"incoming message from {message.get_sender_contact().addr} id={message.id} chat={message.chat.id} text={message.text[:50]!r}" ) self.db.put_msg(message.id) # message is now in DB, schedule a check self._needs_check.set()
def poll_list(message: Message, replies: Replies) -> None: """Show your public polls.""" with session_scope() as session: text = "" for poll in (session.query(Poll).filter_by( addr=message.get_sender_contact().addr).all()): if len(poll.question) > 100: q = poll.question[:100] + "..." else: q = poll.question text += "📊 /poll_get_{} {}\n\n".format(poll.id, q) replies.add(text=text or "❌ Empty list", chat=message.get_sender_chat())
def vote(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Vote in polls. """ args = payload.split() if len(args) not in (2, 3): replies.add(text='Invalid syntax') return if len(args) == 3: chat = bot.get_chat(int(args[0])) if message.get_sender_contact() not in chat.get_contacts(): replies.add(text='You are not a member of that group') return pid = int(args[1]) oid = int(args[2]) - 1 else: chat = message.chat pid = int(args[0]) oid = int(args[1]) - 1 addr = message.get_sender_contact().addr poll = db.get_gpoll_by_id(pid) if poll and chat.id == poll['gid']: if poll['status'] == Status.CLOSED: replies.add(text='That poll is closed') elif db.get_gvote(pid, addr): replies.add(text='You already voted') elif oid not in [opt['id'] for opt in db.get_goptions(pid)]: replies.add(text='Invalid option number') else: db.add_gvote(poll['id'], addr, oid) replies.add(text=format_gpoll(poll, voted=True)) elif len(args) == 2: poll = db.get_poll_by_id(pid) if poll: if poll['status'] == Status.CLOSED: replies.add(text='That poll is closed') elif db.get_vote(pid, addr): replies.add(text='You already voted') elif oid not in [opt['id'] for opt in db.get_options(pid)]: replies.add(text='Invalid option number') else: is_admin = addr == poll['addr'] db.add_vote(poll['id'], addr, oid) replies.add(text=_format_poll( bot, poll, voted=True, is_admin=is_admin)) else: replies.add(text='Invalid poll id') else: replies.add(text='Invalid poll id')
def filter_messages(bot: DeltaBot, message: Message, replies: Replies) -> None: """Process turns in Exquisite Corpse game groups """ if not message.chat.is_group(): sender = message.get_sender_contact() g = db.get_game_by_turn(sender.addr) if g is None: return if len(message.text.split()) < 10: text = '❌ Text too short. Send a message with at least 10 words' replies.add(text=text) else: paragraph = g['text'] + ' ' + message.text db.set_text(g['gid'], paragraph) p = db.get_player_by_addr(sender.addr) assert p is not None if p['round'] == 3: db.delete_player(p['addr']) else: db.set_player(p['addr'], p['round'] + 1, g['gid']) p = _get_by_round(g['gid']) if p is None: # End Game text = _end_game(g['gid']) replies.add(text=text, chat=bot.get_chat(g['gid'])) else: db.set_turn(g['gid'], p['addr']) _run_turn(bot, p, bot.get_chat(g['gid']), paragraph)
def corpse_join(bot: DeltaBot, message: Message, replies: Replies) -> None: """Join in a Exquisite Corpse game Example: `/corpse_join` """ sender = message.get_sender_contact() gid = message.chat.id g = db.get_game_by_gid(gid) if not message.chat.is_group(): replies.add(text='❌ This is not a group.') return if g is None: replies.add(text='❌ There is not a game running in this group.') return player = db.get_player_by_addr(sender.addr) if player: if player['game'] == g['gid']: replies.add(text='❌ You already joined this game.') else: replies.add(text='❌ You are already playing in another group.') return if g['turn'] and db.get_player_by_addr(g['turn'])['round'] != 1: replies.add(text="⌛ Too late!!! You can't join the game at this time") return db.add_player(sender.addr, 1, gid) replies.add(text=_show_status(bot, gid, g['turn']))
def join(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Join the given IRC channel.""" sender = message.get_sender_contact() if not payload: replies.add(text="Wrong syntax") return if not bot.is_admin(sender.addr) and \ not db.is_whitelisted(payload): replies.add(text="That channel isn't in the whitelist") return g = bot.get_chat(db.get_chat(payload)) if g and sender in g.get_contacts(): replies.add(text='You are already a member of this group', chat=g) return if g is None: chat = bot.create_group(payload, [sender]) db.add_channel(payload, chat.id) irc_bridge.join_channel(payload) irc_bridge.preactor.join_channel(sender.addr, payload) else: _add_contact(g, sender) chat = bot.get_chat(sender) nick = db.get_nick(sender.addr) text = '** You joined {} as {}'.format(payload, nick) replies.add(text=text, chat=chat)
def w(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Search in Wikipedia.""" sender = message.get_sender_contact().addr lang = _get_locale(bot, sender) url = "https://{}.m.wikipedia.org/wiki/?search={}".format( lang, quote_plus(payload)) replies.add(**_download_file(bot, url, _get_mode(bot, sender)))
def ddg(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Search in DuckDuckGo.""" mode = _get_mode(bot, message.get_sender_contact().addr) page = 'lite' if mode == 'htmlzip' else 'html' url = "https://duckduckgo.com/{}?q={}".format(page, quote_plus(payload)) replies.add(**_download_file(bot, url, mode))
def lines_play(bot: DeltaBot, message: Message, replies: Replies) -> None: """Start a new Color Lines game. Example: `/lines_play` """ player = message.get_sender_contact() if not db.get_nick(player.addr): text = "You need to set a nick before start playing," text += " send /lines_nick Your Nick" replies.add(text=text) return game = db.get_game_by_addr(player.addr) if game is None: # create a new chat chat = bot.create_group('🌈 Color Lines', [player.addr]) db.add_game(player.addr, chat.id, Board().export()) text = 'Hello {}, in this group you can play Color Lines.\n\n' replies.add(text=text.format(player.name) + _run_turn(chat.id), chat=chat) else: db.set_board(game['addr'], Board(old_score=game['score']).export()) if message.chat.id == game['gid']: chat = message.chat else: chat = bot.get_chat(game['gid']) replies.add(text='Game started!\n\n' + _run_turn(game['gid']), chat=chat)
def lines_top(message: Message, replies: Replies) -> None: """Send Color Lines scoreboard. Example: `/lines_top` """ limit = 15 text = '🏆 Color Lines Scoreboard\n\n' game = db.get_game_by_addr(message.get_sender_contact().addr) if not game: games = db.get_games(limit) else: games = db.get_games() if not games: text += '(Empty list)' for n, g in enumerate(games[:limit], 1): text += '#{} {} {}\n'.format(n, db.get_nick(g['addr']), g['score']) if game: player_pos = games.index(game) if player_pos >= limit: text += '\n' if player_pos > limit: pgame = games[player_pos - 1] text += '#{} {} {}\n'.format(player_pos, db.get_nick(pgame['addr']), pgame['score']) text += '#{} {} {}\n'.format(player_pos + 1, db.get_nick(game['addr']), game['score']) if player_pos < len(games) - 1: ngame = games[player_pos + 1] text += '#{} {} {}\n'.format(player_pos + 2, db.get_nick(ngame['addr']), ngame['score']) replies.add(text=text)
def login(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: sender = message.get_sender_contact() with session_scope() as session: acc = session.query(Account).filter_by(addr=sender.addr).first() # noqa if acc: replies.add(text="❌ You are already logged in.") return args = payload.split(maxsplit=2) if len(args) == 3: client = wf.client(host=args[0], user=args[1], password=args[2]) else: client = wf.client(host=args[0], token=args[1]) acc = Account(addr=sender.addr, host=client.host, token=client.token) for blog in client.get_collections(): group = bot.create_group(f"📝 {blog['title'] or blog['alias']}", [sender]) acc.blogs.append(Blog(chat_id=group.id, alias=blog["alias"])) replies.add( text="All messages sent here will be published to blog:\n" f"Alias: {blog['alias']}\nDescription: {blog['description']}", chat=group, ) with session_scope() as session: session.add(acc) # noqa replies.add(text="✔️Logged in")
def quote(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Get Wikiquote quotes. Search in Wikiquote or get the quote of the day if no text is given. Example: `/quote Richard Stallman` """ locale = _get_locale(bot, message.get_sender_contact().addr) if locale in wq.supported_languages(): lang = locale else: lang = None if payload: authors = wq.search(payload, lang=lang) if authors: if payload.lower() == authors[0].lower(): author = authors[0] else: author = choice(authors) text = f'"{choice(wq.quotes(author, max_quotes=200, lang=lang))}"\n\n― {author}' else: text = f"No quote found for: {payload}" else: _quote, author = wq.quote_of_the_day(lang=lang) text = f'"{_quote}"\n\n― {author}' replies.add(text=text)
def c4_play(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Invite a friend to play Connect4. Example: `/c4_play [email protected]` """ if not payload: replies.add(text="Missing address") return if payload == bot.self_contact.addr: replies.add(text="Sorry, I don't want to play") return p1 = message.get_sender_contact().addr p2 = payload if p1 == p2: replies.add(text="You can't play with yourself") return g = db.get_game_by_players(p1, p2) if g is None: # first time playing with p2 chat = bot.create_group( '4️⃣ {} 🆚 {} [c4]'.format(p1, p2), [p1, p2]) b = Board() db.add_game(p1, p2, chat.id, b.export(), p1) text = 'Hello {1},\nYou have been invited by {0} to play Connect4' text += '\n\n{2}: {0}\n{3}: {1}\n\n' text = text.format( p1, p2, b.get_disc(BLACK), b.get_disc(WHITE)) replies.add(text=text + _run_turn(chat.id), chat=chat) else: text = 'You already have a game group with {}'.format(p2) replies.add(text=text, chat=bot.get_chat(g['gid']))
def poll_get(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Get poll with given id. """ args = payload.split() if len(args) not in (1, 2): replies.add(text='Invalid syntax') return if len(args) == 2: chat = bot.get_chat(int(args[0])) payload = args[1] if message.get_sender_contact() not in chat.get_contacts(): replies.add(text='You are not a member of that group') return else: chat = message.chat pid = int(payload) poll = db.get_gpoll_by_id(pid) if poll and chat.id == poll['gid']: closed = poll['status'] == Status.CLOSED replies.add(text=format_gpoll(poll, closed=closed)) elif len(args) == 1: poll = db.get_poll_by_id(pid) if poll: closed = poll['status'] == Status.CLOSED replies.add(text=_format_poll(bot, poll, closed=closed)) else: replies.add(text='Invalid poll id') else: replies.add(text='Invalid poll id')
def filter_messages(bot: DeltaBot, message: Message, replies: Replies) -> None: """Detect messages like +1 or -1 to increase/decrease score. """ if not message.quote: return score = _parse(message.text) if not score: return sender = message.get_sender_contact().addr is_admin = bot.is_admin(sender) if score < 0 and not is_admin: return if not is_admin and db.get_score(sender) - score < 0: replies.add(text="❌ You can't give what you don't have...", quote=message) return receiver = message.quote.get_sender_contact().addr if sender == receiver: return sender_score = _add_score(sender, -score) receiver_score = _add_score(receiver, score) if is_admin: text = '{0}: {1}{4}' else: text = '{0}: {1}{4}\n{2}: {3}{4}' text = text.format( bot.get_contact(receiver).name, receiver_score, bot.get_contact(sender).name, sender_score, _getdefault(bot, 'score_badge')) replies.add(text=text, quote=message)
def poll_list(message: Message, replies: Replies) -> None: """Show group poll list or your public polls if sent in private. """ if message.chat.is_group(): polls = db.get_gpolls_by_gid(message.chat.id) if polls: text = '' for poll in polls: if len(poll['question']) > 100: q = poll['question'][:100] + '...' else: q = poll['question'] text += '📊 /poll_get_{}_{} {}\n\n'.format( poll['gid'], poll['id'], q) replies.add(text=text) else: replies.add(text='Empty list') else: polls = db.get_polls_by_addr(message.get_sender_contact().addr) if polls: text = '' for poll in polls: if len(poll['question']) > 100: q = poll['question'][:100] + '...' else: q = poll['question'] text += '📊 /poll_get_{} {}\n\n'.format(poll['id'], q) replies.add(text=text) else: replies.add(text='Empty list')
def poll_new(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Create a new public poll. Example: /poll_new Do you like polls? yes no maybe """ lines = [] for ln in payload.split("\n"): ln = ln.strip() if ln: lines.append(ln) if len(lines) < 3: replies.add(text="❌ Invalid poll, at least two options needed") return question = lines.pop(0) poll = Poll(addr=message.get_sender_contact().addr, question=question) for i, opt in enumerate(lines, 1): poll.options.append(Option(id=i, text=opt)) with session_scope() as session: session.add(poll) session.flush() text, html = _format_poll(bot, poll) replies.add(text=text, html=html)
def wttr(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Search weather info from wttr.in""" lang = _get_locale(bot, message.get_sender_contact().addr) url = 'https://wttr.in/{}_Fnp_lang={}.png'.format(quote(payload), lang) reply = _download_file(bot, url) reply.pop('text') replies.add(**reply)
def group_topic(bot: DeltaBot, args: list, message: Message, replies: Replies) -> None: """Show or change group/channel topic. """ if not message.chat.is_group(): replies.add(text='❌ This is not a group') return if args: new_topic = ' '.join(args) max_size = int(_getdefault(bot, 'max_topic_size')) if len(new_topic) > max_size: new_topic = new_topic[:max_size] + '...' text = '** {} changed topic to:\n{}' ch = db.get_channel(message.chat.id) if ch and ch['admin'] == message.chat.id: name = _get_name(message.get_sender_contact()) text = text.format(name, new_topic) db.set_channel_topic(ch['id'], new_topic) for chat in _get_cchats(bot, ch['id']): replies.add(text=text, chat=chat) replies.add(text=text) return if ch: replies.add(text='❌ Only channel operators can do that.') return addr = message.get_sender_contact().addr g = db.get_group(message.chat.id) if not g: _add_group(bot, message.chat.id, as_admin=bot.is_admin(addr)) g = db.get_group(message.chat.id) assert g is not None db.upsert_group(g['id'], new_topic) replies.add(text=text.format(addr, new_topic)) return g = db.get_channel(message.chat.id) or db.get_group(message.chat.id) if not g: addr = message.get_sender_contact().addr _add_group(bot, message.chat.id, as_admin=bot.is_admin(addr)) g = db.get_group(message.chat.id) assert g is not None replies.add(text=g['topic'] or '-', quote=message)
def web(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Download a webpage or file.""" mode = _get_mode(bot, message.get_sender_contact().addr) try: replies.add(**_download_file(bot, payload, mode)) except FileTooBig as err: replies.add(text=str(err))
def cmd_read(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Download a webpage and try to improve its readability.""" mode = _get_mode(bot, message.get_sender_contact().addr) try: replies.add(**_download_file(bot, payload, mode, True)) except FileTooBig as err: replies.add(text=str(err))
def friends_leave(message: Message, replies: Replies) -> None: """Remove you from the list. """ addr = message.get_sender_contact().addr if db.get_bio(addr) is None: replies.add(text='You are not in the list yet') else: db.remove_user(addr) replies.add(text='You were removed from the list')
def poll_settings(args: str, message: Message, replies: Replies) -> None: """Get poll's advanced settings.""" if args: try: with session_scope() as session: poll = (session.query(Poll).filter_by( id=int(args[0]), addr=message.get_sender_contact().addr).one()) text = "📊 /poll_get_{0}\n{1}\n\n🛑 /poll_end_{0}".format( poll.id, poll.question) replies.add(text=text, chat=message.get_sender_chat()) except NoResultFound: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat()) else: replies.add(text="❌ Invalid poll", quote=message, chat=message.get_sender_chat())
def me(payload: str, message: Message, replies: Replies) -> None: """Send a message to IRC using the /me IRC command. """ chan = db.get_channel_by_gid(message.chat.id) if not chan: return addr = message.get_sender_contact().addr text = ' '.join(payload.split('\n')) irc_bridge.preactor.send_action(addr, chan, text)
def corpse_leave(bot: DeltaBot, message: Message, replies: Replies) -> None: """Leave Exquisite Corpse game in current group. Example: `/corpse_leave` """ p = db.get_player_by_addr(message.get_sender_contact().addr) if p: _remove_from_game(bot, p, db.get_game_by_gid(p['game'])) else: replies.add(text='❌ You are not playing Exquisite Corpse.')
def poll_status(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Get poll status. """ args = payload.split() if len(args) not in (1, 2): replies.add(text='Invalid syntax') return if len(args) == 2: chat = bot.get_chat(int(args[0])) payload = args[1] if message.get_sender_contact() not in chat.get_contacts(): replies.add(text='You are not a member of that group') return else: chat = message.chat pid = int(payload) addr = message.get_sender_contact().addr poll = db.get_gpoll_by_id(pid) if poll and chat.id == poll['gid']: voted = db.get_gvote(poll['id'], addr) is not None if voted: closed = poll['status'] == Status.CLOSED replies.add(text=format_gpoll(poll, voted=voted, closed=closed)) else: replies.add(text="You can't see poll status until you vote") elif len(args) == 1: poll = db.get_poll_by_id(pid) if poll: is_admin = addr == poll['addr'] voted = is_admin or db.get_vote(poll['id'], addr) is not None if voted: closed = poll['status'] == Status.CLOSED replies.add(text=_format_poll( bot, poll, voted=voted, closed=closed, is_admin=is_admin)) else: replies.add(text="You can't see poll status until you vote") else: replies.add(text='Invalid poll id') else: replies.add(text='Invalid poll id')
def poll_new(bot: DeltaBot, payload: str, message: Message, replies: Replies) -> None: """Create a new poll in the group it is sent, or a public poll if sent in private. Example: /poll_new Do you like polls? yes no maybe """ lns = payload.split('\n') if len(lns) < 3: replies.add(text='Invalid poll, at least two options needed') return lines = [] for ln in lns: ln = ln.strip() if ln: lines.append(ln) question = lines.pop(0) if len(question) > 255: replies.add(text='Question can have up to 255 characters') return for opt in lines: if len(opt) > 150: replies.add(text='Up to 150 characters per option are allowed') return if message.chat.is_group(): gid = message.chat.id poll = db.get_gpoll_by_question(gid, question) if poll: replies.add(text='Group already has a poll with that name') return db.add_gpoll(gid, question) poll = db.get_gpoll_by_question(gid, question) assert poll is not None for i, opt in enumerate(lines): db.add_goption(i, poll['id'], opt) replies.add(text=format_gpoll(poll)) else: addr = message.get_sender_contact().addr poll = db.get_poll_by_question(addr, question) if poll: replies.add(text='You already have a poll with that name') return db.add_poll(addr, question, time.time()) poll = db.get_poll_by_question(addr, question) assert poll is not None for i, opt in enumerate(lines): db.add_option(i, poll['id'], opt) replies.add(text=_format_poll(bot, poll))