#!/usr/bin/env python # FightBot IRC bot - based on acidvegas skeleton in python import argparse import asyncio import logging import logging.handlers import ssl import sqlite3 import random import time # Formatting Control Characters / Color Codes bold = '\x02' italic = '\x1D' underline = '\x1F' reverse = '\x16' reset = '\x0f' white = '00' black = '01' blue = '02' green = '03' red = '04' brown = '05' purple = '06' orange = '07' yellow = '08' light_green = '09' cyan = '10' light_cyan = '11' light_blue = '12' pink = '13' grey = '14' light_grey = '15' conn = sqlite3.connect('data.db') c = conn.cursor() def createtable(): c.execute('''CREATE TABLE if not exists users ( name text unique, level integer, experience integer, health integer, attack integer, defense integer, money integer, healthmax integer )''') c.execute('''CREATE TABLE if not exists weapons ( name text unique, weaponheld integer, clip integer, ammunition integer, expire integer )''') c.execute('''CREATE TABLE if not exists items ( name text unique, bandages integer )''') c.execute('''CREATE TABLE if not exists stats ( name text unique, kills integer, deaths integer )''') class Users: def __init__(self, name, level, experience, health, attack, defense, money, healthmax): self.name = name self.level = level self.experience = experience self.health = health self.attack = attack self.defense = defense self.money = money self.healthmax = healthmax class Weapons: def __init__(self, name, weaponheld, clip, ammunition): self.name = name self.weaponheld = weaponheld self.clip = clip self.ammunition = ammunition self.expire = 0 class Items: def __init__(self, name, bandages): self.name = name self.bandages = bandages async def addkillstat(name): c.execute(f'SELECT rowid FROM stats WHERE name=(:name)', {'name': name}) checkrow=c.fetchone() if checkrow == None: kills = 1 deaths = 0 c.execute(f'INSERT OR REPLACE INTO stats VALUES (:name, :kills, :deaths)', {'name': name, 'kills': kills, 'deaths': deaths}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {color(name, light_green)} has a total of {color(kills, red)} kills!]') else: c.execute(f'SELECT * FROM stats WHERE name=(:name)', {'name': name}) getkills=c.fetchall() for retrieve in getkills: kills = retrieve[1] + 1 c.execute(f'UPDATE stats SET KILLS = (:kills) WHERE name=(:name)', {'kills': kills, 'name': name}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {color(name, light_green)} has a total of {color(kills, red)} kills!]') async def adddeathstat(name): c.execute(f'SELECT rowid FROM stats WHERE name=(:name)', {'name': name}) checkrow=c.fetchone() if checkrow == None: kills = 0 deaths = 1 c.execute(f'INSERT OR REPLACE INTO stats VALUES (:name, :kills, :deaths)', {'name': name, 'kills': kills, 'deaths': deaths}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Opponent Player {color(name, red)} has died {color(deaths, red)} times!]') else: c.execute(f'SELECT * FROM stats WHERE name=(:name)', {'name': name}) getkills=c.fetchall() for retrieve in getkills: kills = retrieve[1] deaths = retrieve[2] + 1 c.execute(f'UPDATE stats SET DEATHS = (:deaths) WHERE name=(:name)', {'deaths': deaths, 'name': name}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {color(name, red)} has died {color(deaths, red)} times!]') async def buybandages(name, amount): if amount == 0 or amount == None: await bot.sendmsg(config.irc.channel, f'[Must specify how many bandages you want... !buy bandages ]') c.execute(f'SELECT rowid FROM items WHERE name=(:name)', {'name': name}) checkrow=c.fetchone() if checkrow == None: bandages = 0 c.execute(f'INSERT OR REPLACE INTO items VALUES (:name, :bandages)', {'name': name, 'bandages': bandages}) conn.commit() shopamount = amount * 50 c.execute(f'SELECT * FROM items WHERE name=(:name)', {'name': name}) look = c.fetchall() for band in look: bandages = band[1] c.execute(f'SELECT * FROM users WHERE name=(:name)', {'name': name}) usermoney = c.fetchall() for grabmoney in usermoney: money = grabmoney[6] if money >= shopamount: await bot.sendmsg(config.irc.channel, f'[You have enough money [${money:,}] to purchase [{amount}] bandages! Cost is ${shopamount:,}]') else: await bot.sendmsg(config.irc.channel, f'[You do {color("NOT", red)} have enough funds!]') return bandages = band[1] bandagestotal = bandages + amount c.execute(f'UPDATE ITEMS set BANDAGES = (:bandages) WHERE NAME = (:name)', {'bandages': bandagestotal, 'name': name}) conn.commit() money = money - shopamount c.execute(f'UPDATE users set MONEY = (:money) WHERE NAME = (:name)', {'money': money, 'name': name}) conn.commit() async def heal(name): c.execute(f'SELECT * from USERS WHERE name= (:name)', {'name': name}) newtest=c.fetchall() for userinfo in newtest: health = userinfo[3] if health <= 0: await bot.sendmsg(config.irc.channel, f'[You are dead... !revive to bring yourself back]') return c.execute(f'SELECT rowid FROM items WHERE name = (:name)', {'name': name}) havebandages=c.fetchone() if havebandages == None: await bot.sendmsg(config.irc.channel, f'[You do not have any bandages... !buy bandages ]') return c.execute(f'SELECT * FROM items WHERE name = (:name)', {'name': name}) bandaids = c.fetchall() for bandages in bandaids: if bandages[1] <= 0: await bot.sendmsg(config.irc.channel, f'[You do not have any bandages... !buy bandages ]') return maxhealth = userinfo[7] if health == maxhealth: await bot.sendmsg(config.irc.channel, f'[You already at FULL HEALTH]') return newhealth = health + 25 if newhealth >= maxhealth: health = maxhealth c.execute(f'UPDATE USERS set HEALTH = (:health) where NAME = (:name)', {'health': health, 'name': name}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player healed {color("+25", light_green)} to MAX health {color(health, light_green)}]') else: c.execute(f'UPDATE USERS set HEALTH = (:health) where NAME = (:name)', {'health': newhealth, 'name': name}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player healed {color("+25", light_green)} to health {color(newhealth, light_green)}]') c.execute(f'SELECT * FROM items WHERE name=(:name)', {'name': name}) bandaids=c.fetchall() for checkitems in bandaids: bandages = checkitems[1] bandages = bandages - 1 c.execute(f'UPDATE ITEMS set BANDAGES = (:bandages) WHERE name = (:name)', {'bandages': bandages, 'name': name}) conn.commit() async def checkexist(player): c.execute(f"SELECT rowid FROM users WHERE name=(:name)", {'name': player}) data=c.fetchone() if data is None: await bot.sendmsg(config.irc.channel, 'Invalid Player: %s'%player) return False else: return True async def reload(nick): c.execute(f"SELECT rowid FROM users WHERE name=(:name)", {'name': nick}) data=c.fetchone() if data == None: logging.debug('User is trying to reload without having an account') await bot.sendmsg(config.irc.channel, f'[User {nick} does not have an account]') return c.execute(f"SELECT * FROM weapons WHERE name=(:name)", {'name': nick}) data=c.fetchall() name = nick for check in data: hasweapon = check[1] if hasweapon == False: logging.debug('User does not have weapon') await bot.sendmsg(config.irc.channel, f'[You do not currently have a weapon.. !buy weapon]') return maxclip = 30 clip = check[2] ammunition = check[3] if ammunition <= 0: await bot.sendmsg(config.irc.channel, f'!buy ammo') return clipadd = 30 - clip if ammunition >= clipadd: ammunition = ammunition - clipadd clip = clip + clipadd else: clip = clip + ammunition ammunition = 0 logging.debug(f'[Current clip ammunition is at {clip} rounds and backup ammunition is at {ammunition} magazine rouunds!]') await bot.sendmsg(config.irc.channel, f'[Current clip has {clip} rounds out of {maxclip} mag ammo size and {ammunition} in the arsenal!]') c.execute(f"UPDATE WEAPONS set CLIP = (:clip) WHERE NAME = (:name)", {'clip': clip, 'name': nick}) conn.commit() c.execute(f"UPDATE WEAPONS set AMMUNITION = (:ammunition) WHERE NAME = (:name)", {'ammunition': ammunition, 'name': name}) conn.commit() async def shoot(player, nick): check = await checkexist(player) if check is False: return else: if player == nick: logging.debug('Player cannot attack oneself') await bot.sendmsg(config.irc.channel, '[Error: {1}]'.format(0,color('Cannot Attack Yourself', red))) return c.execute(f'SELECT rowid FROM weapons WHERE name=(:name)', {'name': nick}) checkrow=c.fetchone() if checkrow == None: await bot.sendmsg(config.irc.channel, f'[Error: {color("No Weapons", red)}] - !buy weapon') return c.execute(f"SELECT * FROM users WHERE name=(:name)", {'name': nick}) healthcheck = c.fetchall() for checkalive in healthcheck: alive = checkalive[3] if alive <= 0: await bot.sendmsg(config.irc.channel, f'[Error: {color("You Are Dead... !revive", red)}]') return c.execute(f"SELECT * FROM users WHERE name=(:name)", {'name': player}) playerhealthcheck = c.fetchall() for checkalive in playerhealthcheck: health = checkalive[3] if health <= 0: logging.debug('Player targeted is DEAD') await bot.sendmsg(config.irc.channel, f'[Player targeted is {color("DEAD", light_blue)}]') return c.execute(f"SELECT * FROM weapons WHERE name=(:name)", {'name': nick}) weapons = c.fetchall() for reg in weapons: name = nick weaponheld = reg[1] expire = reg[4] c.execute(f"SELECT rowid FROM weapons WHERE name=(:name)", {'name': name}) if weaponheld == 0: await bot.sendmsg(config.irc.channel, 'You do not own a weapon!') return clip = reg[2] ammunition = reg[3] if clip == 0: logging.debug('Weapon out of ammo! !buy ammo and !reload') c.execute("INSERT OR REPLACE INTO weapons VALUES (:name, :weaponheld, :clip, :ammunition, :expire)", {'name': nick, 'weaponheld': weaponheld, 'clip': clip, 'ammunition': ammunition, 'expire': expire}) conn.commit() await bot.sendmsg(config.irc.channel, f'{color("OUT OF AMMO - !reload", red)}') else: c.execute("SELECT * FROM weapons WHERE name=(:name)", {'name': name}) checkexpire=c.fetchall() for expiring in checkexpire: expires = expiring[4] if expires == 300: logging.debug('User weapon has expired Weapon and Ammo Purged. !buy weapon') await bot.sendmsg(config.irc.channel, f'[Player weapon has expired... purging weapon! for a new one]') c.execute('''UPDATE WEAPONS set WEAPONHELD = False, CLIP = 0, AMMUNITION = 0, EXPIRE = 0 WHERE name = (:name)''', {'name': name}) conn.commit() return clip = clip - 3 c.execute("INSERT OR REPLACE INTO weapons VALUES (:name, :weaponheld, :clip, :ammunition, :expire)", {'name': nick, 'weaponheld': weaponheld, 'clip': clip, 'ammunition': ammunition, 'expire': expire}) conn.commit() shooter = c.execute("SELECT * FROM users WHERE name=(:name)", {'name': player}) for reg in shooter: playerhealth = reg[3] damage = random.randint(80, 120) damage = min(damage, int(playerhealth)) playerhealth = int(playerhealth) - int(damage) expire = expire + 1 c.execute("UPDATE WEAPONS set EXPIRE = (:expire) where NAME = (:name)", {'expire': expire, 'name': name}) conn.commit() if playerhealth <= 0: await bot.sendmsg(config.irc.channel, '[Player {1} killed {2} and did {3} damage]'.format(0, color(nick, light_green), color(player, red), color(int(damage), light_green))) await addmoney(nick) await addkillstat(nick) await adddeathstat(player) playerhealth = 0 c.execute('UPDATE USERS set HEALTH = (:health) where NAME = (:name)', {'health': playerhealth, 'name': player}) conn.commit() return c.execute('UPDATE USERS set HEALTH = (:health) where NAME = (:name)', {'health': playerhealth, 'name': player}) conn.commit() #await bot.sendmsg(config.irc.channel, f'[{player} health is now {playerhealth} and took {damage} damage!]') await bot.sendmsg(config.irc.channel, '[Character: {1}] shot at [{2}] with an M16 and did [{3}] damage!'.format(0, color(nick, light_green), color(player, red), color(int(damage), green))) await bot.sendmsg(config.irc.channel, f'[Remaining clip magazine rounds: {clip}]') async def shop(): logging.debug('Printing shop items!') await bot.sendmsg(config.irc.channel, f'[Shop Accessories]') await bot.sendmsg(config.irc.channel, f'[!buy weapon - purchase a 3-round burst gun (note: weapon has a quality of 300 trigger limit)]') await bot.sendmsg(config.irc.channel, f'[!buy ammo - buy ammunition (+60 rounds) for your weapon]') await bot.sendmsg(config.irc.channel, f'[!buy bandages ]') # await bot.sendmsg(config.irc.channel, f'[!buy bandages - buys bandages for health] {color("* not done yet", red)}') async def buyammo(nick): c.execute(f"SELECT * FROM weapons WHERE name=:name", {'name': nick}) weapons = c.fetchall() c.execute(f"SELECT * FROM users WHERE name=:name", {'name': nick}) user = c.fetchall() for reg in user: name = nick money = reg[6] newmoney = money - 50 c.execute("UPDATE USERS set MONEY = (:money) where NAME = (:name)", {'money': newmoney, 'name': name}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {name} purchased 60 rounds of ammunition!]') for weps in weapons: ammunition = weps[3] weaponheld = weps[1] clip = weps[2] ammunition += 60 expire = weps[4] c.execute("INSERT OR REPLACE INTO weapons VALUES (:name, :weaponheld, :clip, :ammunition, :expire)", {'name': nick, 'weaponheld': weaponheld, 'clip': clip, 'ammunition': ammunition, 'expire': expire}) conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {nick} weapon ammunition is now {ammunition}]') async def removeweapon(nick): remweapon = Weapons(nick, 0, 0, 0) c.execute("UPDATE WEAPONS set WEAPONHELD = (:weaponheld) where NAME = (:name)", {'weaponheld': 0, 'name': nick}) conn.commit() logging.debug('Removed weapon') async def buyweapon(nick): c.execute(f"SELECT * FROM weapons WHERE name=:name", {'name': nick}) weapons = c.fetchall() c.execute(f"SELECT * FROM users WHERE name=:name", {'name': nick}) user = c.fetchall() for regs in user: name = nick money = regs[6] for weapon in weapons: weaponheld = weapon[1] if weaponheld == 1: await bot.sendmsg(config.irc.channel, "[Weapon is already held, !buy ammo for more ammunition]") logging.debug('Weapon is already held, returning') return clip = 30 ammunition = 60 money = money - 500 c.execute("UPDATE USERS set MONEY = (:money) where NAME = (:name)", {'money': money, 'name': nick}) conn.commit() logging.debug(f'Attempting to buy Weapon for {nick}') weaponheld = True await bot.sendmsg(config.irc.channel, f"[Player: {nick} has purchased a gun for $500! Loaded clip of {clip} rounds!]") logging.debug('Buying weapon') expire = 0 c.execute("INSERT OR REPLACE INTO weapons VALUES (:name, :weaponheld, :clip, :ammunition, :expire)", {'name': nick, 'weaponheld': weaponheld, 'clip': clip, 'ammunition': ammunition, 'expire': expire}) conn.commit() async def removeuser(nick): logging.debug(f'Removing user {nick}') name = nick c.execute('''DELETE FROM users WHERE name=?''',(name,)) conn.commit() c.execute('''DELETE FROM items WHERE name=?''',(name,)) conn.commit() c.execute('''DELETE FROM weapons WHERE name=?''',(name,)) conn.commit() c.execute('''DELETE FROM stats WHERE name=?''',(name,)) conn.commit() logging.debug(f'Removed user {nick}') async def createuser(nick): logging.debug(f'Attemping to create user {nick}') reguser = Users(nick, 1, 500, 500, 100, 100, 5000, 500) logging.debug(f'User {nick} added to database') c.execute("INSERT OR REPLACE INTO users VALUES (:name, :level, :experience, :health, :attack, :defense, :money, :healthmax)", {'name': nick, 'level': reguser.level, 'experience': reguser.experience, 'health': reguser.health, 'attack': reguser.attack, 'defense': reguser.defense, 'money': reguser.money, 'healthmax': reguser.healthmax}) conn.commit() c.execute(f'SELECT rowid FROM stats WHERE name=(:name)', {'name': nick}) checkrow=c.fetchone() if checkrow == None: kills = 0 deaths = 0 c.execute(f'INSERT OR REPLACE INTO stats VALUES (:name, :kills, :deaths)', {'name': nick, 'kills': kills, 'deaths': deaths}) conn.commit() #await bot.sendmsg(config.irc.channel, f'[Player {color(name, light_green)} has a total of {color(kills, red)} kills!]') async def setlevel(nick, level): if level == '1': experience = '500' health = '500' attack = '100' defense = '100' healthmax = '500' c.execute(f'''UPDATE USERS set LEVEL = (:level), EXPERIENCE = (:experience), HEALTH = (:health), ATTACK = (:attack), DEFENSE = (:defense), HEALTHMAX = (:healthmax) where NAME = (:name)''', {'level': level, 'experience': experience, 'health': health, 'attack': attack, 'defense': defense, 'name': nick, 'healthmax': healthmax}) conn.commit() await bot.sendmsg(config.irc.channel, f"[Player: {nick} is now level {color(level, red)}]") elif level == '2': experience = '1000' health = '1000' attack = '150' defense = '150' healthmax = '1000' c.execute(f'''UPDATE USERS set LEVEL = (:level), EXPERIENCE = (:experience), HEALTH = (:health), ATTACK = (:attack), DEFENSE = (:defense), HEALTHMAX = (:healthmax) where NAME = (:name)''', {'level': level, 'experience': experience, 'health': health, 'attack': attack, 'defense': defense, 'name': nick, 'healthmax': healthmax}) conn.commit() await bot.sendmsg(config.irc.channel, f"[Player: {nick} is now level {color(level, red)}]") elif level == '3': experience = '2000' health = '1500' attack = '175' defense = '175' healthmax = '1500' c.execute(f'''UPDATE USERS set LEVEL = (:level), EXPERIENCE = (:experience), HEALTH = (:health), ATTACK = (:attack), DEFENSE = (:defense), HEALTHMAX = (:healthmax) where NAME = (:name)''', {'level': level, 'experience': experience, 'health': health, 'attack': attack, 'defense': defense, 'name': nick, 'healthmax': healthmax}) conn.commit() await bot.sendmsg(config.irc.channel, f"[Player: {nick} is now level {color(level, red)}]") elif level == '4': experience = '3000' health = '2000' attack = '200' defense = '200' healthmax = '2000' c.execute(f'''UPDATE USERS set LEVEL = (:level), EXPERIENCE = (:experience), HEALTH = (:health), ATTACK = (:attack), DEFENSE = (:defense), HEALTHMAX = (:healthmax) where NAME = (:name)''', {'level': level, 'experience': experience, 'health': health, 'attack': attack, 'defense': defense, 'name': nick, 'healthmax': healthmax}) conn.commit() await bot.sendmsg(config.irc.channel, f"[Player: {nick} is now level {color(level, red)}]") elif level == '5': experience = '5000' health = '3000' attack = '225' defense = '225' healthmax = '3000' c.execute(f'''UPDATE USERS set LEVEL = (:level), EXPERIENCE = (:experience), HEALTH = (:health), ATTACK = (:attack), DEFENSE = (:defense), HEALTHMAX = (:healthmax) where NAME = (:name)''', {'level': level, 'experience': experience, 'health': health, 'attack': attack, 'defense': defense, 'name': nick, 'healthmax': healthmax}) conn.commit() await bot.sendmsg(config.irc.channel, f"[Player: {nick} is now level {color(level, red)}]") elif int(level) > 5: await bot.sendmsg(config.irc.channel, "[Error: Invalid Level]") return async def closesql(): conn.close() class combat: class attacker: name = '' attack = '' class defender: name = '' defense = '' health = '' async def punch(player, nick): check = await checkexist(player) if check is False: return else: for name in (nick): c.execute(f"SELECT * FROM users WHERE name=:name", {'name': nick}) attacker = c.fetchall() c.execute(f"SELECT * FROM users WHERE name=:name", {'name': player}) defender = c.fetchall() if nick == player: await bot.sendmsg(config.irc.channel, '[Error: {1}]'.format(0,color('Cannot Attack Yourself', red))) return for att in attacker: combat.attacker.name = att[0] combat.attacker.attack = att[4] combat.attacker.health = att[3] if combat.attacker.health == 0: await bot.sendmsg(config.irc.channel, '[Error: You Are {1}!]'.format(0, color('Dead', red))) return for defend in defender: combat.defender.name = defend[0] combat.defender.defense = defend[5] combat.defender.health = defend[3] playerhealth = combat.defender.health if combat.defender.health <= 0: await bot.sendmsg(config.irc.channel, f'[Player targeted is {color("DEAD", light_blue)}]') return playerdef = combat.defender.defense playerattack = combat.attacker.attack critical_multiplier = 1.1 if random.random() < 0.10 else 1.0 effective_defense = int(playerdef) * random.uniform(0.9, 1.1) effective_attack = int(playerattack) * random.uniform(1.0, 1.1) damage = effective_attack * (50 / (50 + effective_defense)) * critical_multiplier damage = min(damage, int(playerhealth)) combat.defender.health = int(playerhealth) - int(damage) c.execute(f'''UPDATE USERS set HEALTH = (:health) where NAME = (:name)''', {'health': combat.defender.health, 'name': combat.defender.name}) conn.commit() if combat.defender.health <= 0: await bot.sendmsg(config.irc.channel, '[Player {1} killed {2} and did {3} damage]'.format(0, color(nick, light_green), color(player, red), color(int(damage), light_green))) await addmoney(nick) await addkillstat(nick) await adddeathstat(player) combat.defender.health = 0 return await bot.sendmsg(config.irc.channel, '[Character: {1}] Punched [{2}] and did [{3}] damage!'.format(0, color(nick, light_green), color(player, red), color(int(damage), green))) c.execute(f'''UPDATE USERS set HEALTH = (:health) where NAME = (:name)''', {'health': combat.defender.health, 'name': combat.defender.name}) conn.commit() async def profile(nick): check = await checkexist(nick) if check is False: return try: c.execute(f"SELECT * FROM users WHERE name=(:name)", {'name': nick}) reg = c.fetchall() for regs in reg: name = regs[0] level = regs[1] experience = regs[2] health = regs[3] attack = regs[4] defense = regs[5] money = regs[6] healthmax = regs[7] c.execute(f'SELECT * FROM stats WHERE name=(:name)', {'name': name}) grabstats=c.fetchall() for stats in grabstats: kills = stats[1] deaths = stats[2] await bot.sendmsg(config.irc.channel, f'[Player: {color(name, light_green)}] [Level: {color(level, red)}] [Experience: {color(experience, blue)}] [Health: {color(health, light_green)}/{color(healthmax, green)}] [Att/Def: {color(attack, light_green)}/{color(defense, red)}] [Money: {color(money, light_blue)}] [Kills: {color(kills, light_blue)}] [Deaths: {color(deaths, red)}]') except Exception as ex: print('Error {0}'.format(0, ex)) #bot.sendmsg(config.irc.channel, '[Error: No Such User]') async def revive(nick): check = await checkexist(nick) if check is False: return c.execute("SELECT * FROM users WHERE name=(:name)", {'name': nick}) checkhealth=c.fetchall() for amount in checkhealth: health = amount[3] max = amount[7] if health <= 0: c.execute("UPDATE USERS set HEALTH = (:health) WHERE NAME = (:name)", {'health': max, 'name': nick}) logging.debug('test') conn.commit() await bot.sendmsg(config.irc.channel, f'[Player {color(nick,light_green)} has been revived!]') else: await bot.sendmsg(config.irc.channel, f'[You are not DEAD! No reviving the living!]') class config: class irc: server = 'irc.supernets.org' port = 6667 username = 'vortex' realname = 'FightBot by vorteckz' nickname = 'FightBot' channel = '#dev' channelkey = 'test' admins = {'vortex', 'vorteckz','acidvegas','peanuter'} class throttle: cmd = 0.5 msg = 0.5 reconnect = 10 rejoin = 3 last = 0 slow = False lastnick = None def color(msg: str, foreground: str, background: str='') -> str: ''' Color a string with the specified foreground and background colors. :param msg: The string to color. :param foreground: The foreground color to use. :param background: The background color to use. ''' return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}' def ssl_ctx() -> ssl.SSLContext: '''Create a SSL context for the connection.''' ctx = ssl.create_default_context() #ctx.verify_mode = ssl.CERT_NONE # Comment out this line to verify hosts #ctx.load_cert_chain('/path/to/cert', password='loldongs') return ctx async def getmoney(nick): c.execute(f"SELECT * FROM users WHERE name=(:name)", {'name': nick}) moneygrab = c.fetchall() for money in moneygrab: moneyamount = money[6] writemoney = f'{moneyamount:,}' await bot.sendmsg(config.irc.channel, f'[Bank Balance: ${color(writemoney, light_blue)}]') async def getbandages(nick): name = nick c.execute(f'SELECT rowid FROM items WHERE name=(:name)', {'name': nick}) data=c.fetchone() if data is None: bandages = 0 c.execute(f'INSERT OR REPLACE INTO items VALUES (:name, :bandages)', {'name': name, 'bandages': bandages}) conn.commit() c.execute(f"SELECT * FROM items WHERE name=(:name)", {'name': nick}) bandages = c.fetchall() for bandage in bandages: bandageamount = bandage[1] if bandageamount == None: logging.debug('test') bandageamount = 0 writebandageamount = f'{bandageamount:,}' await bot.sendmsg(config.irc.channel, f'[Bandages: {color(writebandageamount, red)}]') async def ammo(nick): c.execute(f'SELECT rowid FROM users WHERE name=(:name)', {'name': nick}) exist = c.fetchone() if exist is None: await bot.sendmsg(config.irc.channel, f'[User {nick} does not have an account!]') return c.execute(f"SELECT * FROM weapons WHERE name=(:name)", {'name': nick}) checkammo = c.fetchall() for ammo in checkammo: hasweapon = ammo[1] if hasweapon == False: await bot.sendmsg(config.irc.channel, f'[You do not currently have a weapon.. !buy weapon]') return ammoclipcount = ammo[2] ammomagcount = ammo[3] await bot.sendmsg(config.irc.channel, f"[Ammo Count] [Current Clip Holding: {ammoclipcount}] [Arsenal Magazine Rounds: {ammomagcount}]") async def addmoney(nick): c.execute(f"SELECT * FROM users WHERE name=:name", {'name': nick}) moneygrab = c.fetchall() for money in moneygrab: moneyamount = money[6] profit = 500 moneyamount += profit moneyamountformat = f'{moneyamount:,}' await bot.sendmsg(config.irc.channel, f'[You gained ${color(profit, blue)}!] [Bank Balance: ${color(moneyamountformat, light_blue)}]') c.execute(f'''UPDATE USERS set MONEY = (:money) where NAME = (:name)''', {'money': moneyamount, 'name': nick}) conn.commit() class Bot(): def __init__(self): self.nickname = config.irc.nickname self.username = config.irc.username self.realname = config.irc.realname self.channel = config.irc.channel self.channelkey = config.irc.channelkey self.reader = None self.writer = None createtable() async def action(self, chan: str, msg: str): ''' Send an ACTION to the IRC server. :param chan: The channel to send the ACTION to. :param msg: The message to send to the channel. ''' await self.sendmsg(chan, f'\x01ACTION {msg}\x01') async def raw(self, data: str): ''' Send raw data to the IRC server. :param data: The raw data to send to the IRC server. (512 bytes max including crlf) ''' self.writer.write(data[:510].encode('utf-8') + b'\r\n') async def sendmsg(self, target: str, msg: str): ''' Send a PRIVMSG to the IRC server. :param target: The target to send the PRIVMSG to. (channel or user) :param msg: The message to send to the target. ''' try: await self.raw(f'PRIVMSG {target} :{msg}') time.sleep(config.throttle.msg) except: await bot.sendmsg(config.irc.channel, "Slow down homie!!") async def connect(self): '''Connect to the IRC server.''' while True: try: options = { 'host' : args.server, 'port' : args.port if args.port else 6697 if args.ssl else 6667, 'limit' : 1024, # Buffer size in bytes (don't change this unless you know what you're doing) 'ssl' : ssl_ctx() if args.ssl else None, 'family' : 10 if args.v6 else 2, # 10 = AF_INET6 (IPv6), 2 = AF_INET (IPv4) 'local_addr' : args.vhost if args.vhost else None # Can we just leave this as args.vhost? } self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), 15) # 15 second timeout if args.password: await self.raw('PASS ' + args.password) # Rarely used, but IRCds may require this await self.raw(f'USER {self.username} 0 * :{self.realname}') # These lines must be sent upon connection await self.raw('NICK ' + self.nickname) # They are to identify the bot to the server while not self.reader.at_eof(): data = await asyncio.wait_for(self.reader.readuntil(b'\r\n'), 300) # 5 minute ping timeout await self.handle(data.decode('utf-8').strip()) # Handle the data received from the IRC server except Exception as ex: logging.error(f'failed to connect to {args.server} ({str(ex)})') finally: await asyncio.sleep(30) # Wait 30 seconds before reconnecting async def handle(self, data: str): ''' Handle the data received from the IRC server. :param data: The data received from the IRC server. ''' try: logging.info(data) args = data.split() if data.startswith('ERROR :Closing Link:'): raise Exception('Cannot Connect') if args[0] == 'PING': await self.raw('PONG ' + args[1]) # Respond to the server's PING request with a PONG to prevent ping timeout elif args[1] == '001': # RPL_WELCOME await self.raw(f'MODE {self.nickname} +B') # Set user mode +B (Bot) await asyncio.sleep(10) # Wait 10 seconds before joining the channel (required by some IRCds to wait before JOIN) await self.raw(f'JOIN {self.channel} {self.channelkey}') elif args[1] == '433': # ERR_NICKNAMEINUSE self.nickname += '_' # If the nickname is already in use, append an underscore to the end of it await self.raw('NICK ' + self.nickname) # Send the new nickname to the server elif args[1] == 'KICK': chan = args[2] kicked = args[3] if kicked == self.nickname: await asyncio.sleep(3) await self.raw(f'JOIN {chan}') elif args[1] == 'PRIVMSG': ident = args[0][1:] nick = args[0].split('!')[0][1:].lower() target = args[2] msg = ' '.join(args[3:])[1:] arguments = msg.split() bandageamount = '0' if target == self.nickname: pass # Handle private messages here if target.startswith('#'): # Channel message if msg.startswith('!'): try: if time.time() - config.throttle.last < config.throttle.cmd and config.throttle.lastnick == nick: if not config.throttle.slow: config.throttle.slow = True await bot.sendmsg(config.irc.channel, color("Slow down homie!!", red)) else: config.throttle.slow = False config.throttle.lastnick = nick if arguments[0] == '!hug': await bot.sendmsg(target, f'[XOXO Hugger9000... {nick} hugs {arguments[1]}]') if arguments[0] == '!admins': for i in config.irc.admins: await bot.sendmsg(target, f'[Admins: ' + color(i, red) + ']') if arguments[0] == '!shop': await shop() if arguments[0] == '!buy' and arguments[1] != None: if arguments[1] == 'weapon': await buyweapon(nick) elif arguments[1] == 'ammo': await buyammo(nick) elif arguments[1] == 'bandages': if len(arguments) <= 2 or int(arguments[2]) == 0: await bot.sendmsg(config.irc.channel, '[You must specify amount of bandages (greater than 0) to purchase]') else: await buybandages(nick, int(arguments[2])) if arguments[0] == '!shoot': await shoot(arguments[1].lower(), nick) if arguments[0] == '!reload': await reload(nick) if arguments[0] == '!ammo': await ammo(nick) if arguments[0] == '!heal': await heal(nick) if arguments[0] == '!revive': await revive(nick) if arguments[0] == '!help': await bot.sendmsg(config.irc.channel, '[Command List]') await bot.sendmsg(config.irc.channel, '[!help - shows commands]') await bot.sendmsg(config.irc.channel, '[!register - Register your player]') await bot.sendmsg(config.irc.channel, '[!profile - Shows Profile Stats [!profile username]') await bot.sendmsg(config.irc.channel, '[!punch - Fight [!punch username]') await bot.sendmsg(config.irc.channel, '[!shoot - Shoot if you own a weapon]') await bot.sendmsg(config.irc.channel, '[!bank - Returns Bank Balance]') await bot.sendmsg(config.irc.channel, '[!buy [weapon, ammo, bandages ]') await bot.sendmsg(config.irc.channel, '[!reload - Reloads Weapon]') await bot.sendmsg(config.irc.channel, '[!ammo - Show Ammunition Amounts]') await bot.sendmsg(config.irc.channel, '[!bandages - Shows Bandage Amounts]') await bot.sendmsg(config.irc.channel, '[!revive - Brings you back to health if dead]') await bot.sendmsg(config.irc.channel, '[!heal - Use bandages to regain health]') await bot.sendmsg(config.irc.channel, ' ') await bot.sendmsg(config.irc.channel, '[Admin Command List]') await bot.sendmsg(config.irc.channel, '[!setlevel - !setlevel username Level (1 to 5)]') await bot.sendmsg(config.irc.channel, '[!adduser - Force create a user]') await bot.sendmsg(config.irc.channel, '[!remove - Removes a player]') await bot.sendmsg(config.irc.channel, '[!removeweapon - Remove user weapon]') if arguments[0] == '!bank': await getmoney(nick) if arguments[0] == '!bandages': await getbandages(nick) if nick in config.irc.admins: #await self.sendmsg(target, f'{nick} is an ' + color('Admin!', red)) if arguments[0] == '!removeweapon': await removeweapon(nick) if arguments[0] == '!adduser': name = arguments[1].lower() c.execute(f"SELECT rowid FROM users WHERE name = (:name)", {'name': name}) data=c.fetchone() if data is None: await bot.sendmsg(config.irc.channel, '[Registering Player: %s]'%name) await createuser(name) await profile(name) else: await bot.sendmsg(config.irc.channel, f'{color("[Player already exists!]", red)}') if arguments[0] == '!remove': logging.debug('remove user') name = arguments[1].lower() c.execute(f"SELECT rowid FROM users WHERE name= (:name)", {'name': name}) data=c.fetchone() if data != None: await bot.sendmsg(config.irc.channel, f'[Removing {color(name, red)} from database]') await removeuser(name) else: await bot.sendmsg(config.irc.channel, f'[User does not exist]') if arguments[0] == '!setlevel': await setlevel(arguments[1].lower(), arguments[2]) if arguments[0] == '!register': c.execute(f"SELECT rowid FROM users WHERE name = (:name)", {'name': nick}) data=c.fetchone() if data is None: await bot.sendmsg(config.irc.channel, f'[Registering Player: %s]'%nick) await createuser(nick) await profile(nick) else: await bot.sendmsg(config.irc.channel, f'{color("[Player already exists!]", red)}') if arguments[0] == '!profile': try: await profile(arguments[1].lower()) except: await profile(nick) if arguments[0] == '!punch': await punch(arguments[1].lower(), nick) config.throttle.last = time.time() except Exception as ex: if time.time() - config.throttle.last < config.throttle.cmd: if not config.throttle.slow: await bot.sendmsg(config.irc.channel, color('Slow down homie!', red)) config.throttle.slow = True config.throttle.last = time.time() #except (UnicodeDecodeError, UnicodeEncodeError): #pass # Some IRCds allow invalid UTF-8 characters, this is a very important exception to catch except Exception as ex: logging.exception(f'Unknown error has occured! ({ex})') def setup_logger(log_filename: str, to_file: bool = False): ''' Set up logging to console & optionally to file. :param log_filename: The filename of the log file ''' sh = logging.StreamHandler() sh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(message)s', '%I:%M %p')) if to_file: fh = logging.handlers.RotatingFileHandler(log_filename+'.log', maxBytes=250000, backupCount=3, encoding='utf-8') # Max size of 250KB, 3 backups fh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(filename)s.%(funcName)s.%(lineno)d | %(message)s', '%Y-%m-%d %I:%M %p')) # We can be more verbose in the log file logging.basicConfig(level=logging.NOTSET, handlers=(sh,fh)) else: logging.basicConfig(level=logging.NOTSET, handlers=(sh,)) if __name__ == '__main__': parser = argparse.ArgumentParser(description="Connect to an IRC server.") # The arguments without -- are required arguments. parser.add_argument("server", help="The IRC server address.") parser.add_argument("channel", help="The IRC channel to join.") parser.add_argument("--password", help="The password for the IRC server.") parser.add_argument("--port", type=int, help="The port number for the IRC server.") # Port is optional, will default to 6667/6697 depending on SSL. parser.add_argument("--ssl", action="store_true", help="Use SSL for the connection.") parser.add_argument("--v4", action="store_true", help="Use IPv4 for the connection.") parser.add_argument("--v6", action="store_true", help="Use IPv6 for the connection.") parser.add_argument("--key", default="", help="The key (password) for the IRC channel, if required.") parser.add_argument("--vhost", help="The VHOST to use for connection.") args = parser.parse_args() config.irc.channel = args.channel if args.key: config.irc.channelkey = args.key print(f"Connecting to {args.server}:{args.port} (SSL: {args.ssl}) and joining {args.channel} (Key: {args.key or 'None'})") setup_logger('skeleton', to_file=True) # Optionally, you can log to a file, change to_file to False to disable this. bot = Bot() # We define this here as an object so we can call it from an outside function if we need to. asyncio.run(bot.connect())