diff --git a/Mafia.py b/Mafia.py index c83cb26..a7442c0 100644 --- a/Mafia.py +++ b/Mafia.py @@ -93,7 +93,6 @@ def wrapper(*args, **kwargs): item.reply(stm['err']['notStarted']) return -1 - if (func.__name__ != 'burnUser'): if pattern: search = pattern.group(2) @@ -440,6 +439,66 @@ def unlockTier(): item.reply(stm['err']['wrongCode']) return -1 + @log_commit + @game_command + def switchTeam(): + if (cfg['commands']['allowSwitchTeam'] == 0): + item.reply(stm['err']['switchTeamDisabled']) + return -1 + + con.execute(stm['preStm']['unlock'][0], (item.author.name,)) + r = con.fetchall() + + if (r[0][0] < cfg['commands']['unlockInviteSwitch']): + item.reply(stm['err']['notUnlocked']) + return -1 + + pattern = re.search(r'^!convert\s(?:u/)?([A-Za-z0-9_]{1,20})', item.body) + target = pattern.group(1) + con.execute(stm['preStm']['digupUser'], (target,)) + r = con.fetchall() + + if ((len(r) <= 0) or (r[0][2]) != 1): + item.reply(stm['err']['notAlive']) + return -1 + + con.execute(stm['preStm']['switchTeam'][0], (target,)) + r = con.fetchall() + + if (len(r) > 1): + item.reply(stm['err']['switchTeamBlocked']) + return -1 + + con.execute(stm['preStm']['switchTeam'][1], (item.author.name, target)) + success = con.rowcount + + if (success > 0): + sendMessage(target, stm['reply']['switchTeamMsg'].format(item.author.name, curCycle + 1)) + item.reply(stm['reply']['switchTeam']) + reddit.submission(id=cfg['reddit']['targetPost']).reply(stm['comment']['actions']['switchTeamInvite']) + else: + item.reply(stm['err']['switchTeam']) + return -1 + + @log_commit + def acceptInvite(): + if (cfg['commands']['allowSwitchTeam'] == 0): + item.reply(stm['err']['switchTeamDisabled']) + return -1 + + con.execute(stm['preStm']['switchTeam'][2], (item.author.name,)) + r = con.fetchall() + + if (len(r) <= 0): + item.reply(stm['err']['switchTeamNone']) + return -1 + + con.execute(stm['preStm']['switchTeam'][3], (r[0][0], r[0][1])) + con.execute(stm['preStm']['switchTeam'][4], (r[0][1],)) + item.reply(stm['reply']['switchTeamAccept']) + sendMessage(r[0][0], stm['reply']['switchTeamAccepted'].format(r[0][1])) + reddit.submission(id=cfg['reddit']['targetPost']).reply(stm['comment']['actions']['switchTeam']) + @log_commit def getList(): dead = '' @@ -475,7 +534,7 @@ def getStats(): if (len(r) == 1): team = stm['teams'][0][r[0][0]] - tier = stm['teams'][1][r[0][0]][r[0][1]] + tier = stm['teams'][2][r[0][0]][r[0][1]] loc = r[0][2] status = stm['alive'][r[0][3]] @@ -693,6 +752,7 @@ def cycle(curCycle): sleep(0.2) con.execute('TRUNCATE TABLE VoteCall'); + con.execute('TRUNCATE TABLE TeamInvite;'); comment = reddit.submission(id=cfg['reddit']['targetPost']).reply(stm['sticky']['cycle'].format(curCycle + 2, alive, good, bad, killed, alive + killed)) comment.mod.distinguish(how='yes', sticky=True) if (item != None): item.reply(f'**Moved to Round {curCycle + 2}**') @@ -712,7 +772,7 @@ def broadcast(): if (cfg['commands']['allowBotBroadcast'] == 0): item.reply('Broadcast Disabled') - return + return -1 con.execute(stm['preStm']['getAll']) r = con.fetchall() @@ -730,6 +790,7 @@ def restart(): con.execute(stm['preStm']['restart']) con.execute('TRUNCATE TABLE VoteCall;'); + con.execute('TRUNCATE TABLE TeamInvite;'); con.execute('COMMIT;') comment = reddit.submission(id=cfg['reddit']['targetPost']).reply(stm['sticky']['restart']) comment.mod.distinguish(how='yes', sticky=True) @@ -756,6 +817,7 @@ def reset(): con.execute('TRUNCATE TABLE Mafia;'); con.execute('TRUNCATE TABLE VoteCall;'); + con.execute('TRUNCATE TABLE TeamInvite;'); con.execute(stm['preStm']['log'], (item.created_utc, item.author.name, 'reset')) con.execute('COMMIT;') comment = reddit.submission(id=cfg['reddit']['targetPost']).reply(stm['sticky']['reset']) @@ -875,6 +937,10 @@ def sendMessage(name, message): requestUser() elif (re.search(r'^!unlock', item.body)): unlockTier() + elif (re.search(r'^!convert', item.body)): + switchTeam() + elif (re.search(r'^!accept', item.body)): + acceptInvite() elif ((re.search(r'^!list', item.body))): getList() elif (re.search(r'^!stats', item.body)): @@ -921,16 +987,20 @@ def save(state, curCycle): def setItems(k, v): tmp = {} - if (os.path.getsize('data/items.pickle') > 0): - with open('data/items.pickle', 'rb') as itemsFile: - tmp = pickle.load(itemsFile) + try: + if (os.path.getsize('data/items.pickle') > 0): + with open('data/items.pickle', 'rb') as itemsFile: + tmp = pickle.load(itemsFile) + tmp[k] = v + else: + print('WARNING items.pickle not found. Creating new one.') tmp[k] = v - else: + except: print('WARNING items.pickle not found. Creating new one.') tmp[k] = v - - with open('data/items.pickle', 'wb') as itemsFile: - pickle.dump(tmp, itemsFile) + finally: + with open('data/items.pickle', 'wb') as itemsFile: + pickle.dump(tmp, itemsFile) def getItems(k): if os.path.getsize('data/items.pickle') > 0: diff --git a/README.md b/README.md index 4793693..82c8085 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ Play against other members to find out who amongst each other you can trust and |The Twelve|Trainee|1|Access to digup (low accuracy), vote, and max 3 requests.| |The Twelve|Assassin|2|Digup accuracy increased.| |The Twelve|Handler|3|Digup accuracy increased. Unlocked locate and burn command (Tier 3 or lower). 3 Additional requests. Can escape hits.| -|The Twelve|Keeper|4|Revive unlocked. Burn does not announce who you are.| +|The Twelve|Keeper|4|Revive and convert unlocked. Burn does not announce who you are.| |MI6|Recruit|1|Access to digup (low accuracy), vote, and max 3 requests.| |MI6|Analyst|2|Digup accuracy increased.| |MI6|Operative|3|Digup accuracy increased. Unlocked locate and burn command (Tier 3 or lower). 3 Additional requests. Can escape hits.| -|MI6|Supervisor|4|Revive unlocked. Burn does not announce who you are.| +|MI6|Supervisor|4|Revive and convert unlocked. Burn does not announce who you are.| **Commands** @@ -54,10 +54,12 @@ All commands must be sent privately to u/DozenIncBOT. To use investigative comma |!vote USERNAME|Vote on who to eliminate. Can not be changed once voted per round.|2| |!digup USERNAME|Investigate the roles of other users. Has varying degree of reliability based on your tier|1| |!locate USERNAME|Shows the user's location. May give a clue as to what side they are on.|3| +|!convert USERNAME|Invite a user to switch to your team.|3| +|!accept|Accept an invitation to switch teams.|1| +|!unlock CODE|Enter a secret code in order to level up to the next tier.|1| |!stats|Gets the current stats for the game.|1| |!help|Shows all commands available.|1| |!rules|Shows the rules.|1| -|!unlock CODE|Enter a secret code in order to level up to the next tier.|1| **Flairs** diff --git a/init/database.sql b/init/database.sql index 0b3088f..88794d3 100644 --- a/init/database.sql +++ b/init/database.sql @@ -1,5 +1,5 @@ -- MySQL Script generated by MySQL Workbench --- Wed Jul 8 00:41:18 2020 +-- Wed Jul 22 22:17:27 2020 -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering @@ -19,15 +19,6 @@ DROP SCHEMA IF EXISTS `Reddit` ; -- Schema Reddit -- ----------------------------------------------------- CREATE SCHEMA IF NOT EXISTS `Reddit` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin ; --- ----------------------------------------------------- --- Schema reddit --- ----------------------------------------------------- -DROP SCHEMA IF EXISTS `reddit` ; - --- ----------------------------------------------------- --- Schema reddit --- ----------------------------------------------------- -CREATE SCHEMA IF NOT EXISTS `reddit` ; USE `Reddit` ; -- ----------------------------------------------------- @@ -50,24 +41,37 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- DROP TABLE IF EXISTS `Reddit`.`Mafia` ; -CREATE TABLE `Mafia` ( - `utc` int NOT NULL, - `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, - `team` int NOT NULL DEFAULT '-1', - `tier` int NOT NULL DEFAULT '0', - `loc` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `alive` int NOT NULL DEFAULT '1', - `diedOnCycle` int DEFAULT NULL, - `burn` int NOT NULL DEFAULT '1', - `revive` int NOT NULL DEFAULT '1', - `request` int NOT NULL DEFAULT '3', - `comment` int NOT NULL DEFAULT '0', - `inactive` int NOT NULL DEFAULT '0', - PRIMARY KEY (`utc`), - UNIQUE KEY `Mafiacol_UNIQUE` (`username`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin +CREATE TABLE IF NOT EXISTS `Reddit`.`Mafia` ( + `utc` INT NOT NULL, + `username` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL, + `team` INT NOT NULL DEFAULT '-1', + `tier` INT NOT NULL DEFAULT '0', + `loc` VARCHAR(255) NULL DEFAULT NULL, + `alive` INT NOT NULL DEFAULT '1', + `diedOnCycle` INT NULL DEFAULT NULL, + `burn` INT NOT NULL DEFAULT '1', + `revive` INT NOT NULL DEFAULT '1', + `request` INT NOT NULL DEFAULT '3', + `comment` INT NOT NULL DEFAULT '0', + `inactive` INT NOT NULL DEFAULT '0', + PRIMARY KEY (`utc`)) +ENGINE = InnoDB +DEFAULT CHARACTER SET = utf8 +COLLATE = utf8_bin; -CREATE UNIQUE INDEX `Mafiacol_UNIQUE` ON `Reddit`.`Mafia` (`username` ASC) VISIBLE; + +-- ----------------------------------------------------- +-- Table `Reddit`.`TeamInvite` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `Reddit`.`TeamInvite` ; + +CREATE TABLE IF NOT EXISTS `Reddit`.`TeamInvite` ( + `username` VARCHAR(20) NOT NULL, + `target` VARCHAR(20) NOT NULL, + PRIMARY KEY (`username`)) +ENGINE = InnoDB +DEFAULT CHARACTER SET = utf8 +COLLATE = utf8_bin; -- ----------------------------------------------------- @@ -76,16 +80,13 @@ CREATE UNIQUE INDEX `Mafiacol_UNIQUE` ON `Reddit`.`Mafia` (`username` ASC) VISIB DROP TABLE IF EXISTS `Reddit`.`VoteCall` ; CREATE TABLE IF NOT EXISTS `Reddit`.`VoteCall` ( - `username` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL, - `vote` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL, + `username` VARCHAR(20) NOT NULL, + `vote` VARCHAR(20) NOT NULL, PRIMARY KEY (`username`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; -CREATE UNIQUE INDEX `username_UNIQUE` ON `Reddit`.`VoteCall` (`username` ASC) VISIBLE; - -USE `reddit` ; USE `Reddit` ; -- ----------------------------------------------------- @@ -126,20 +127,6 @@ ORDER BY team DESC; END$$ DELIMITER ; -USE `reddit` ; - --- ----------------------------------------------------- --- Placeholder table for view `reddit`.`rolecnt` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `reddit`.`rolecnt` (`team` INT, `count` INT); - --- ----------------------------------------------------- --- View `reddit`.`rolecnt` --- ----------------------------------------------------- -DROP TABLE IF EXISTS `reddit`.`rolecnt`; -DROP VIEW IF EXISTS `reddit`.`rolecnt` ; -USE `reddit`; -CREATE OR REPLACE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `Reddit`.`rolecnt` AS select `Reddit`.`Mafia`.`team` AS `team`,count(`Reddit`.`Mafia`.`team`) AS `count` from `Reddit`.`Mafia` where (`Reddit`.`Mafia`.`alive` = '1') group by `Reddit`.`Mafia`.`team` order by `Reddit`.`Mafia`.`team` desc; SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; diff --git a/init/default_settings.json b/init/default_settings.json index cceface..36179a8 100644 --- a/init/default_settings.json +++ b/init/default_settings.json @@ -13,6 +13,7 @@ ], "commands": { "allowBotBroadcast": 1, + "allowSwitchTeam": 1, "addRequests": 3, "addRequestsOn": 1, "burnAfter": 7, @@ -21,6 +22,7 @@ "maxRequests": 3, "reviveAfter": 1, "unlockBurn": 2, + "unlockInviteSwitch": 3, "unlockLocate": 2, "unlockRequest": 0, "unlockRevive": 3, diff --git a/init/settings.json b/init/settings.json index ca5534e..226884e 100644 --- a/init/settings.json +++ b/init/settings.json @@ -15,6 +15,7 @@ ], "commands": { "allowBotBroadcast": 1, + "allowSwitchTeam": 1, "addRequests": 3, "addRequestsOn": 1, "burnAfter": 7, @@ -23,6 +24,7 @@ "maxRequests": 3, "reviveAfter": 1, "unlockBurn": 2, + "unlockInviteSwitch": 3, "unlockLocate": 2, "unlockRequest": 0, "unlockRevive": 3, @@ -39,7 +41,7 @@ "reddit": { "praw": "DozenIncBOT", "sub": "SomethingsNotRight", - "targetPost": "hoohm0" + "targetPost": "hwq25z" }, "sql": { "database": "Reddit", diff --git a/init/statements.json b/init/statements.json index 1763956..2d4cd9d 100644 --- a/init/statements.json +++ b/init/statements.json @@ -15,7 +15,9 @@ "removeUser": "u/{} has left", "requestUser": "A request for intel on u/{} has been filed by {}.", "revive": "A player was revived.", - "schdWarn": "Round ending in {} minutes!" + "schdWarn": "Round ending in {} minutes!", + "switchTeam": "A player has changed their allegiance!", + "switchTeamInvite": "An offer was made to switch allegiances." }, "spook": [ "Trust no one.", @@ -123,6 +125,10 @@ "notUnlocked": "Command not authorized!.\n\nCrack more game codes to unlock this command.", "reviveUsed": "You have already used your abulity to revive.", "spec": "You are a spectator. You cannot use this command.", + "switchTeam": "You already sent an invitation this round", + "switchTeamBlocked": "Someone has already made an offer to that player this round.\n\nYou may try to convince them to reject their existing offer.", + "switchTeamDisabled": "Team switching is disabled.", + "switchTeamNone": "You have no pending invitations.", "unkCmd": "Invalid Command. Type !help for a list of commands.", "unkPM": "Unknown Command. Please enter commands through [private message](https://www.reddit.com/message/compose/?to=DozenIncBOT).", "voteUser": "You already casted your vote for this round.", @@ -227,6 +233,13 @@ "UPDATE Mafia SET `revive`=0 WHERE `username`=%s", "UPDATE Mafia SET `alive`=1 WHERE `username`=%s" ], + "switchTeam": [ + "SELECT `target` FROM TeamInvite WHERE `target`=%s;", + "INSERT INTO TeamInvite (`username`, `target`) VALUES(%s, %s) ON DUPLICATE KEY UPDATE `username`=`username`;", + "SELECT `username`,`target` FROM TeamInvite WHERE `target`=%s;", + "UPDATE Mafia m, (SELECT `team` FROM Mafia WHERE `username`=%s) t SET m.team = t.team WHERE m.username=%s;", + "DELETE FROM TeamInvite WHERE `target`=%s;" + ], "unlock": [ "SELECT `tier`,`team` FROM Mafia WHERE `username`=%s;", "UPDATE Mafia SET `tier` = `tier` + 1 WHERE `username`=%s;", @@ -262,7 +275,7 @@ "getList": "Total players: {}\n\nDead: {}\n{}\n\nAlive: {}\n{}", "getSts": [ [ - "**Current Stats**\n\nGame is {} on round {}.\n\nYou're a/an {} with {}.\nYour location is {}.\nYou are {}.\n\nAlive: {}\n* MI6: {}\n* The Twelve: {}\nKilled: {}\nTotal Players: {}\n\nGame Parameters\n* Burn after round {}\n* Vote threshold: {}\n* Single vote after round {}\n* Max requests: {}\n* Kick inactive after {} rounds" + "**Current Stats**\n\nGame is {} on round {}.\n\nYou're {} with {}.\nYour location is {}.\nYou are {}.\n\nAlive: {}\n* MI6: {}\n* The Twelve: {}\nKilled: {}\nTotal Players: {}\n\nGame Parameters\n* Burn after round {}\n* Vote threshold: {}\n* Single vote after round {}\n* Max requests: {}\n* Kick inactive after {} rounds" ], [ "not active", @@ -284,6 +297,10 @@ "revivedUser": "You have been revived by u/{}!\n\nGet back into the game and win!", "showHelp": "List of available commands:\n* !burn: Exposes one of your teammates randomly for a 100% credible Intelligence Report. Can only be used once\n* !digup USERNAME: Gather intelligence on a user.\n* !help: Shows help.\n* !join: Join the game.\n* !leave: Leave the game. You cannot rejoin once the game has started.\n* !list: Show all players.\n* !locate USERNAME: Shows the location of the user.\n* !request USERNAME: Ask for intel on a player. Limited number of uses.\n* !rules: Shows the rules.\n* !revive USERNAME: Bring back one user back from the dead. Can only be used once.\n* !stats: Shows current number of players.\n* !unlock CODE: Enter a code to level up your rank and gain new investigative powers.\n* !vote USERNAME: Vote on which user to be eliminated.", "showRules": "Rules:\n1. Each user is assigned to either MI6 or the Twelve.\n2. You must investigate each other to determine who is on what side and eliminate the opponent.\n3. Decipher and decrypt the clues provided in order to advance your rank and unlock more powerful investigation tools.\n4. Vote out players who you suspect are the enemy. A player must receive a certain number of hits in order to be killed. After a certain amount of time, the number of votes required will be lowered to 1. Votes, aka hits, only last per round.\n4. At the end of each round, an announcement will be made how many people are alive how many were killed.\n5. You must choose your allies carefully to seek out the enemy.", + "switchTeam": "Invitation Sent.\n\nYou may send another invitation next round.", + "switchTeamAccept": "You have switched teams.", + "switchTeamAccepted": "u/{} has accepted your invitation to switch to your team.", + "switchTeamMsg": "u/{} has invited you to switch to their team. To accep this invitation, use !accept.\n\nThis invitation will expire at the end of this round, round {}.", "voteUser": "Your vote has been locked in for this round." }, "teams": [