def launch(self): """ Botを起動する。 Botが何らかの理由で終了するまで処理は停止する。 """ log("client-login", "ログイン処理を開始します。") self.run(self.setting.token)
async def on_message(self, message: discord.Message): """ メッセージを受信したときに発火されるイベント。 処理対象のメッセージであるかを確認し、適切なコマンドを実行する。 :param message: 受信したメッセージの情報。 """ # 処理対象のメッセージかを確認する if not self.check_response_required(message.channel, message.author): return if message.author.bot or not message.content.startswith( self.setting.prefix): return log("client-msg", "処理対象のメッセージを受信しました:\n{}".format(message.content)) # コマンドを実行する try: await self.command_register.execute_command(message) except Exception: log("client-msg", "コマンド解析中に例外が発生しました!") traceback.print_exc() await message.channel.send( "ああああああああああああああああああああああああああああああああ!!!!!!!!!!!!!!!!!!!!!!!" "```{}```".format(traceback.format_exc()))
async def on_reaction_clear(self, message: discord.Message): """ リアクションが全削除されたときに呼ばれる :param message: 該当するメッセージ """ if self.vote_record.get(message.id) is None: return # お気持ち表明 await message.channel.send( "お前?!?!!??!??!??!?!?!?!おい!??!?!?!?!?!?!??!!?!?!?\n" "いっぱい消すな!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" "今すぐツイッターでメス堕ちしろ!!!!!!!!!!!!!!!おい!!!!!!!!!!!!!!!!!!!!!!!!!") log("react-clr", "ID: {}に関連付けされた投票が全て削除されました。該当する投票を登録から削除します。".format(message.id)) # 整合性がvoidに還ったので消す self.vote_record.delete(message.id) embed: discord.Embed = message.embeds[0] embed.title = "†無効投票 (リアクションぶっち)†" await message.edit(content="この投票は無効投票になりました。", embed=embed) await message.channel.send("投票が全てぶっちされたので、無効投票になってしまいました。お前のせいです。あーあ")
def validate_reaction(self, reaction: discord.Reaction, user: discord.Member) -> ReactionEventRequirement: """ リアクションが適切か確認し、ロールバックが必要かを判断する。 :param reaction: バリデートするリアクション。 :param user: リアクションしたユーザー。 :return: リアクションに対し反応が必要であればRESPOND、 必要なければNO_RESPOND、 ロールバックが必要であればROLLBACKが返る。 正常な動作が行われた場合にゼロ、不正な動作が行われた際に非ゼロでもある。 """ # そのリアクションが関係あるメッセージに送られたものかを書く飲する if self.vote_record.get(reaction.message.id) is None: return ReactionEventRequirement.NO_RESPOND # そのリアクションが適切な絵文字かを確認する if reaction.emoji.id not in self.setting.emoji_ids.values(): log("reaction", "不正なリアクションです。") return ReactionEventRequirement.NO_RESPOND # そのリアクションをした人が参政権を持っているかを確認する if self.setting.suffrage_role_id not in [x.id for x in user.roles]: log("reaction", "不正なユーザーからのリアクションです。ロールバックが必要です。") return ReactionEventRequirement.ROLLBACK # 何も問題なければロールバックは不要 return ReactionEventRequirement.RESPOND
async def on_approved(self, message: discord.Message, vote_record: TweetsVoteRecord): log("on_approved", "可決を確認しました。") new_embed = message.embeds[0] new_embed.title = "†可決†" await message.edit(content="この投票は可決されました!", embed=new_embed) vote_record.delete(message.id)
def initialize_commands(self, guild: discord.Guild, vote_record: TweetsVoteRecord): """ 登録されたコマンドを初期化する。 :param guild: コマンドが実行されるギルド。 :param vote_record: ツイートの投票のレコード。 """ for command in self.commands_type: log("cmd-init", "{} を初期化します…".format(command.__name__)) command_instance = command(guild, self.setting, vote_record) self.commands[command_instance.get_command_info().identify] = command_instance
def read_from_db(column): query = db.execute("select * from connected") for row in query: if column == 'location_bool' and row[1] is not None: return str(row[1]) elif column == 'coordinates' and row[2] is not None: return str(row[2]) elif column == 'ip_addr' and row[3] is not None: return str(row[3]) else: logger.log("ERROR", "Not a known column or DB is empty.") return
async def execute_command(self, text: str, message: discord.Message): log("command-create", "ツイートの作成コマンドを受信しました。") # ツイート内容に問題がないか確認する error_message = validate_tweet(text) if error_message != "": await message.channel.send(error_message) return # 現在のロールの状態を取得する suffrage_role: discord.Role = self.guild.get_role( self.setting.suffrage_role_id) # ツイート内容のデータを生成する suffrage_count = ceil( len(suffrage_role.members) * (self.setting.approve_rate / 100)) tweet_content = TweetVote(text, message.author, suffrage_count) # 投票用のEmbedを作成する embed = create_tweet_vote_embed(tweet_content) embed.set_footer(text="†ACQUIRING ID IN PROGRESS†") sent_message: discord.Message = await message.channel.send( "IDを取得しています…", embed=embed) # 送信して得たIDをEmbedに埋め込む(編集) new_embed = sent_message.embeds[0] new_embed.set_footer(text="ID: †{}†".format(sent_message.id)) await sent_message.edit(content="リアクションを設定しています…", embed=new_embed) # リアクションを設定する await sent_message.add_reaction(await self.guild.fetch_emoji( self.setting.emoji_ids["approve"])) await sent_message.add_reaction(await self.guild.fetch_emoji( self.setting.emoji_ids["deny"])) # ステータスメッセージを設定する await sent_message.edit(content="{}の皆さん、投票のお時間ですわよ!".format( suffrage_role.mention), embed=new_embed) # 保存してDone self.vote_record.add(sent_message.id, tweet_content) log( "command-create", "以下のコンテンツを登録しました:\nID: {}\n{}".format(sent_message.id, tweet_content))
async def on_reaction_remove(self, reaction: discord.Reaction, user: discord.Member): """ リアクションが削除されたときの処理。 :param reaction: リアクションが削除された対象のメッセージの、現在のリアクションの状態 :param user: 誰「の」リアクションが削除されたか (whose) """ # イベントに対してどう反応すべきかを確認する response = self.validate_reaction(reaction, user) # 反応の必要がないか if response == ReactionEventRequirement.NO_RESPOND: return False # ロールバックが必要か if response == ReactionEventRequirement.ROLLBACK: # 削除されるとロールバックでないのでお気持ち表明して帰る await reaction.message.channel.send( "お前!!!!!!!!!!!!!!!!なんてことしてくれたんだ!!!!!!!!!!!!!!!!!!!!!!\n" "***†卍 メス堕ち女装土下座生配信 卍†***奉れ!!!!!!!!!!!!!!!!よ!!!!!!!!!!!!!!!!!!!" ) return log("react-del", "{}がしたリアクションが削除されました。".format(user.name)) # TweetsVoteRecordから該当するTweetVoteを持ってくる tweet_vote = self.vote_record.get(reaction.message.id) # リアクションを基に投票状態を更新する if reaction.emoji.id == self.setting.emoji_ids["approve"]: tweet_vote.approves -= 1 if reaction.emoji.id == self.setting.emoji_ids["deny"]: tweet_vote.denys -= 1 # 反映する self.vote_record.set(reaction.message.id, tweet_vote) # Embedに反映する embed = create_tweet_vote_embed(tweet_vote) embed.set_footer(text="ID: †{}†".format(reaction.message.id)) await reaction.message.edit(embed=embed)
async def execute_command(self, msg: discord.Message): """ メッセージを基にコマンドを実行する。 :param msg: コマンドを実行するために発行されたメッセージ。 """ cmd_header = msg.content.split(" ")[0] cmd_identity = cmd_header[len(self.setting.prefix):] if cmd_identity in ["", "help"]: # コマンドが指定されないか、helpの場合はhelpを送信する await msg.channel.send(self.get_help_message()) return if cmd_identity not in self.commands: # コマンドが見つからない log("cmd-parse", "コマンドが見つかりませんでした: 「{}」".format(cmd_identity)) await msg.channel.send("知らないコマンドが出てきました:thinking:") return # コマンドを実行する await self.commands[cmd_identity].execute_command( msg.content[len(cmd_header) + 1:], msg )
def write_to_db(location_bool, coordinates, ip_addr): if location_bool is None or coordinates is None or ip_addr is None: return elif not re.search("true|false|NULL", location_bool, re.I|re.M): logger.log("ERROR", str(location_bool) + " is not a known mode.") elif not re.search("\A\((\d|\-\d)+\.\d+,\s(\d|\-\d)+\.\d+\)|NULL", coordinates, re.M | re.I): logger.log("ERROR", "Improper coordinate format -> " + str(coordinates) + ".") elif not re.search("\A\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$|NULL", ip_addr, re.M|re.I): logger.log("ERROR", "Improper ip address format -> " + str(ip_addr) + ".") else: coor = re.sub("[\(\)]", "", str(coordinates)) db.execute("insert into connected (location_bool, coordinates, ip_addr) values(\"" + str(location_bool) + "\", \"" + str(coor) + "\", \"" + str(ip_addr) + "\")") db.commit()
async def on_ready(self): """ Botが起動した際に呼ばれる。 必要なチャンネルが存在するかを確認し、コマンドの初期化を行う。 """ log("client-login", "ログインに成功しました。適切な設定が行われているか確認しています。") activity_channel = self.get_channel(self.setting.activity_channel_id) if activity_channel is None: log("client-login", "アクティビティチャンネルが見つかりません。設定に異常があります。") raise RuntimeError( "Activity channel is not found! Check your \"activity_channel_id\" value." ) log("client-login", "設定に問題はありませんでした。コマンドのインスタンスを生成します…") self.command_register.initialize_commands(activity_channel.guild, self.vote_record) log("client-login", "問題は発生しませんでした。起動メッセージを送信します…") await activity_channel.send("***†Delitter Ready†***")
def add_ip_to_db(ip_addr): try: if read_from_db('ip_addr') is None: write_to_db('NULL','NULL', ip_addr) logger.log("INFO", "Writing ip_addr to DB.") elif read_from_db('ip_addr') != ip_addr and read_from_db('ip_addr') is not None: update_db('ip_addr', ip_addr) logger.log("INFO", "Updating ip_addr variable in DB.") else: return except sqlite3.OperationalError: call(['/usr/bin/rm', '/home/' + user.name() + '/.imagecapture/imagecapture.db']) logger.log("ERROR", "The database is locked, could not add IP address to DB.") pass
def add_coordinates_to_db(coordinates): try: if read_from_db('coordinates') is None: write_to_db('NULL', coordinates,'NULL') logger.log("INFO", "Writing coordinates to DB.") elif not read_from_db('coordinates') == coordinates and read_from_db('coordinates') is not None: update_db('coordinates', ip_addr) logger.log("INFO", "Updating coordinates variable in DB.") else: return except sqlite3.OperationalError: call(['/usr/bin/rm', '/home/' + user.name() + '/.imagecapture/imagecapture.db']) logger.log("ERROR", "The database is locked, could not add coordinates to DB.") pass
def add_location_to_db(location_bool): try: if read_from_db('location_bool') is None: write_to_db(location_bool,'NULL','NULL') logger.log("INFO", "Writing location_bool to DB.") elif read_from_db('location_bool') != location_bool and read_from_db('location_bool') is not None: update_db('location_bool', location_bool) logger.log("INFO", "Updating location_bool variable in DB.") else: return except sqlite3.OperationalError: call(['/usr/bin/rm', '/home/' + user.name() + '/.imagecapture/imagecapture.db']) logger.log("ERROR", "The database is locked, could not add location_bool to DB.") pass
def update_db(column,value): if column is None or value is None: return try: if read_from_db('location_bool') is None or read_from_db('coordinates') is None or read_from_db('ip_addr') is None: logger.log("ERROR", "You must write to the database first before updating!") return elif re.search("true|false", value, re.I|re.M) and column == 'location_bool': db.execute("update connected set location_bool = \"" + value + "\"") db.commit() elif re.search("\A(\d|\-\d)+\.\d+,\s(\d|\-\d)+\.\d+", value, re.M | re.I) and column == 'coordinates': db.execute("update connected set coordinates = \"" + value + "\"") db.commit() elif re.search("\A\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$", value, re.M|re.I) and column == 'ip_addr': db.execute("update connected set ip_addr = \"" + value + "\"") db.commit() else: logger.log("ERROR", str(column) + " is not a known column for the connected table in the imagecapture db.") return except sqlite3.OperationalError: logger.log("ERROR", "The database is lock, could not add coordinates to DB.")
def validate_tweet(text: str) -> str: """ ツイート内容に問題がないか確認する :param text: 確認するツイート内容。 :return: 問題が会った場合はエラーメッセージ。問題なければ空文字が返ってくる。 """ apparent_len = get_apparently_length(text) if apparent_len > 240: log("command-create", "文字列が長すぎました。({} > 240)".format(apparent_len)) return "テキストが長すぎるみたいです:thinking:\n" \ "{}文字あって{}文字オーバーしてるので削ってみてください。" \ .format(ceil(apparent_len / 2), ceil(apparent_len / 2 - 240)) if apparent_len < 2: log("command-create", "文字列が極端に短いです。({} < 2)".format(apparent_len)) return "文字列が極端に短いみたいです:thinking:\n" \ "1文字(英数字の場合は2文字です)も入ってないみたいです。" if CreateVoteCommand.SPECIAL_CHARACTER_REGEX.match(text) is not None: log("command-create", "特殊な文字列が含まれています。") return "特殊な文字列が含まれています:thinking:\n" \ "メンションやこの鯖独自の絵文字(<:ahe:724540322322972723>とか)はツイートできません。使えたら面白いんだけどな〜" return ""
#!/usr/bin/env python import sqlite3 import lib.name.user as username import lib.logging.logger as logger user = username.name() DB_PATH = "/home/" + user + "/.imagecapture" DB_FILE = "" + DB_PATH + "/imagecapture.db" db = sqlite3.connect(DB_FILE) try: query = db.execute("select * from connected") except sqlite3.OperationalError: db.execute( '''CREATE TABLE connected(id integer primary key AUTOINCREMENT, location_bool text not null, coordinates text not null, ip_addr text not null);''' ) logger.log("Table(connected) does not exist, creating now.")
def tweet(self, content: str): log("twitter-helper", "以下の内容でツイートが行われました: {}".format(content)) self.twitter.statuses.update(status=content)
#!/usr/bin/env python import sqlite3 import lib.name.user as username import lib.logging.logger as logger user = username.name() DB_PATH = "/home/" + user + "/.imagecapture" DB_FILE = "" + DB_PATH + "/imagecapture.db" db = sqlite3.connect(DB_FILE) try: query = db.execute("select * from connected") except sqlite3.OperationalError: db.execute('''CREATE TABLE connected(id integer primary key AUTOINCREMENT, location_bool text not null, coordinates text not null, ip_addr text not null);''') logger.log("Table(connected) does not exist, creating now.")