Slide 1

Slide 1 text

beproudbot Slack移行 天下一bot武闘会 2015/04/17

Slide 2

Slide 2 text

目次 - はじめに - お前誰よ - 技術的なこと - 実装コード例 - まとめ

Slide 3

Slide 3 text

お前誰よ - altnight - Python/Django - bot担当

Slide 4

Slide 4 text

Skypeつらい - 重い - 重い - 重い - 同期できないときがある - 落ちる - 固まる - 通知できない… - なんとなくイケてない…

Slide 5

Slide 5 text

Slackにしよう - Slackにすれば - 軽くて - 通知もできて - スニペットもはれて - Integrationも便利で - なんかいろいろいい感じで最高では?

Slide 6

Slide 6 text

Slackに移行するには? - アカウントを調べて - 料金や制限を調べて - 必要な人を招待して - 部屋の命名規則などを決めて - beproudbotを動くようにする <- ここの作業

Slide 7

Slide 7 text

概要 - Skypeで動作するbeproudbotをSlackでも動かす - 開発期間は1ヶ月弱(2014/07)

Slide 8

Slide 8 text

beproudbotの歴史 - 1枚のスクリプトだった - djangoアプリ化された - いろいろ機能が増えた - GJ機能を実装していた流れでSlack対応の実装をした

Slide 9

Slide 9 text

What is GJ機能 - 「GJ」なことがあったらインクリメント - デクリメントはなし - “altnight++ bot実装” みたいにチャット上でカジュアルに行 う

Slide 10

Slide 10 text

What is GJ機能

Slide 11

Slide 11 text

What is GJ機能 - 雑談でも?

Slide 12

Slide 12 text

フレームワーク/ライブラリ - Python 2.7(将来Py3移行できるように) - Django 1.5(当時の最新版) - irc - slacker

Slide 13

Slide 13 text

設計 - Djangoアプリで作成する - botの発言イベントの受け取りはircライブラリで行う - 受け取ったイベントメッセージのハンドラーを作る - Skype4Pyで動く既存実装を流用するため、Skype4Pyイベン トと互換性のあるオブジェクトを実装する - 投稿はSlack APIを使用する

Slide 14

Slide 14 text

構成

Slide 15

Slide 15 text

構成

Slide 16

Slide 16 text

イベントの取得をどうする? - outgoingを経由しているプラグインは、プライベートグループ が多いので事実上使えない - -> hubotも使えない(かった) - 自前でポーリング実装する? - Slack API access limit にひっかかるのでは?

Slide 17

Slide 17 text

イベントの取得をどうする? - SlackのIRC gateway接続でIRC接続部分を流用するのが一 番楽だった - 現在は RTM API あります - https://api.slack.com/rtm

Slide 18

Slide 18 text

起動する beproudbot/beproudbot/ircbot/management/commands/runircbot.py logger.info('ircbot start command accepted') # connect to irc client ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket) connection_data = dict(connect_factory=ssl_factory) bot = IRCBot(settings.IRC_CHANNEL, settings.IRC_NICK, settings.IRC_SERVER, settings.IRC_PORT, settings.IRC_PASSWORD, **connection_data) # slack インスタンスをbotに紐付ける slack = slack_utils.get_slack(settings.SLACK_API_TOKEN) setattr(bot, '_slack_instance', slack) # 部屋名一覧をbotに紐付ける room_list = slack_utils.get_rooms(slack) setattr(bot, '_room_list', room_list) # ユーザー一覧をbotに紐付ける user_list = slack_utils.get_users(slack) setattr(bot, '_user_list', user_list) # bot.start の後に埋め込むとログが出ないため手前でロギングする logger.info('ircbot start successfully') # start try: bot.start() except Exception, e: logger.exception('ircbot exception') raise

Slide 19

Slide 19 text

Botインスタンスの作成 beproudbot/beproudbot/ircbot/bot.py class IRCBot(irc.bot.SingleServerIRCBot): def __init__(self, channel, nickname, server, port=6667, password='', **connection_data): irc.bot.SingleServerIRCBot.__init__(self, [(server, port, password)], nickname, nickname, reconnection_interval=60, **connection_data) def make_message_event(self, event): """ IRCで送られてくるイベントからアプリで使う Messageイベントに変換する """ return Message(self, event.sender_unicode, event.body_unicode, event.target_unicode) def process_on_pubmsg(self, event): """ グループの発言を受け取って処理する """ msg = self.make_message_event(event) on_irc_message.dispatch(msg) def on_pubmsg(self, connection, event): """ チャンネル、グループメッセージが送られてきたときのフックポイント """ self.process_on_pubmsg(event)

Slide 20

Slide 20 text

ありがちなこと - アッ

Slide 21

Slide 21 text

Skype4Py互換オブジェクト作成 beproudbot/beproudbot/ircbot/handlers.py class Chat(object): def __init__(self, irc_instance, target): self._irc_instance = irc_instance # 部屋ID self.Name = slack_utils.get_channel_id(target, self._room_list) # 部屋名 self.Topic = target def SendMessage(self, msg): post_message(self._slack_instance, self._target_name, msg, self._irc_instance)

Slide 22

Slide 22 text

Skype4Py互換オブジェクト作成 class Message(object): def __init__(self, irc_instance, who, body, target): self.Sender = Sender(who) self.Body = body self.Chat = Chat(irc_instance, target) self.Timestamp = time.time()

Slide 23

Slide 23 text

Skype4Py互換オブジェクト作成 class Sender(object): def __init__(self, who): self.Handle = who self.FullName = who self.Birthday = ''

Slide 24

Slide 24 text

イベントの伝播 beproudbot/beproudbot/ircbot/handlers.py class IRCHandler(object): def __init__(self): self.receivers = set() def connect(self, receiver): self.receivers.add(receiver) def dispatch(self, message): for receiver in self.receivers: receiver(self, message, "RECEIVED") on_irc_message = IRCHandler()

Slide 25

Slide 25 text

既存コードのレシーバ接続 beproudbot/beproudbot/thx/skypebot.py on_message.connect(thx_receiver) on_irc_message.connect(thx_receiver)

Slide 26

Slide 26 text

いくつか改修して - GJデータ移行 - 部屋モデル作成 - DB migration - コマンドのPrefix変更(# -> $)

Slide 27

Slide 27 text

動いた - ほっ

Slide 28

Slide 28 text

まとめ - 全体的にうまく動いていて一安心 - Slackよい