commit
83d30ceab6
28 changed files with 3534 additions and 0 deletions
@ -0,0 +1,2 @@
|
||||
botdir: /home/example/bot/pyircbot/ |
||||
moduledir: /home/example/bot/pyircbot/modules/ |
@ -0,0 +1,5 @@
|
||||
minBet: .01 |
||||
lobbyIdleSeconds: 15 |
||||
channelWhitelistOn: True |
||||
channelWhitelist: |
||||
- test |
@ -0,0 +1,4 @@
|
||||
host: 127.0.0.1 |
||||
username: root |
||||
password: root |
||||
port: 22555 |
@ -0,0 +1,7 @@
|
||||
hintDelay: 15 |
||||
delayNext: 5 |
||||
maxHints: 5 |
||||
abortAfterNoGuesses: 2 |
||||
winAmount: 0.1 |
||||
categoryduration: 2 |
||||
decreaseFactor: 0.83 |
@ -0,0 +1,3 @@
|
||||
channelWhitelistOn: True |
||||
channelWhitelist: |
||||
- test |
@ -0,0 +1,4 @@
|
||||
host: 127.0.0.1 |
||||
username: root |
||||
password: root |
||||
database: pyircbot |
@ -0,0 +1,5 @@
|
||||
hintDelay: 15 |
||||
delayNext: 5 |
||||
maxHints: 5 |
||||
abortAfterNoGuesses: 5 |
||||
decreaseFactor: 0.83 |
@ -0,0 +1,23 @@
|
||||
user: |
||||
nick: |
||||
- pyircbot3 |
||||
- pyircbot3_ |
||||
- pyircbot3__ |
||||
password: nickservpassword |
||||
username: pyircbot3 |
||||
hostname: pyircbot3.domain.com |
||||
realname: pyircbot3 |
||||
ident: |
||||
enable: yes |
||||
to: nickserv |
||||
command: identify %(password)s |
||||
ghost: yes |
||||
ghost_to: nickserv |
||||
ghost_cmd: ghost %(nick)s %(password)s |
||||
channels: |
||||
- "#pyircbot3" |
||||
privatechannels: |
||||
to: chanserv |
||||
command: invite %(channel)s |
||||
list: |
||||
- "#aprivatechannel" |
@ -0,0 +1,50 @@
|
||||
Alabama |
||||
Alaska |
||||
Arizona |
||||
Arkansas |
||||
California |
||||
Colorado |
||||
Connecticut |
||||
Delaware |
||||
Florida |
||||
Georgia |
||||
Hawaii |
||||
Idaho |
||||
Illinois |
||||
Indiana |
||||
Iowa |
||||
Kansas |
||||
Kentucky |
||||
Louisiana |
||||
Maine |
||||
Maryland |
||||
Massachusetts |
||||
Michigan |
||||
Minnesota |
||||
Mississippi |
||||
Missouri |
||||
Montana |
||||
Nebraska |
||||
Nevada |
||||
New Hampshire |
||||
New Jersey |
||||
New Mexico |
||||
New York |
||||
North Carolina |
||||
North Dakota |
||||
Ohio |
||||
Oklahoma |
||||
Oregon |
||||
Pennsylvania |
||||
Rhode Island |
||||
South Carolina |
||||
South Dakota |
||||
Tennessee |
||||
Texas |
||||
Utah |
||||
Vermont |
||||
Virginia |
||||
Washington |
||||
West Virginia |
||||
Wisconsin |
||||
Wyoming |
@ -0,0 +1,313 @@
|
||||
Air |
||||
Stone |
||||
Grass |
||||
Dirt |
||||
Cobblestone |
||||
Wooden Plank |
||||
Sapling |
||||
Redwood Sapling |
||||
Birch Sapling |
||||
Bedrock |
||||
Water |
||||
Lava |
||||
Sand |
||||
Gravel |
||||
Gold Ore |
||||
Iron Ore |
||||
Coal Ore |
||||
Wood |
||||
Redwood |
||||
Birchwood |
||||
Leaves |
||||
Redwood Leaves |
||||
Birchwood Leaves |
||||
Sponge |
||||
Glass |
||||
Lapis Lazuli Ore |
||||
Lapis Lazuli Block |
||||
Dispenser |
||||
Sandstone |
||||
Note Block |
||||
Bed Block |
||||
Powered Rail |
||||
Detector Rail |
||||
Sticky Piston |
||||
Web |
||||
Dead Shrub |
||||
Tall Grass |
||||
Live Shrub |
||||
Dead Shrub |
||||
Piston |
||||
Piston Head |
||||
White Wool |
||||
Orange Wool |
||||
Magenta Wool |
||||
Light Blue Wool |
||||
Yellow Wool |
||||
Light Green Wool |
||||
Pink Wool |
||||
Gray Wool |
||||
Light Gray Wool |
||||
Cyan Wool |
||||
Purple Wool |
||||
Blue Wool |
||||
Brown Wool |
||||
Dark Green Wool |
||||
Red Wool |
||||
Black Wool |
||||
Dandelion |
||||
Rose |
||||
Brown Mushroom |
||||
Red Mushroom |
||||
Gold Block |
||||
Iron Block |
||||
Double Stone Slab |
||||
Double Sandstone Slab |
||||
Double Wooden Slab |
||||
Double Cobblestone Slab |
||||
Double Brick Slab |
||||
Double Stone Brick Slab |
||||
Stone Slab |
||||
Sandstone Slab |
||||
Wooden Slab |
||||
Cobblestone Slab |
||||
Brick Slab |
||||
Stone Brick Slab |
||||
Brick |
||||
TNT |
||||
Bookshelf |
||||
Mossy Cobblestone |
||||
Obsidian |
||||
Torch |
||||
Fire |
||||
Monster Spawner |
||||
Wooden Stairs |
||||
Chest |
||||
Redstone Wire |
||||
Diamond Ore |
||||
Diamond Block |
||||
Workbench |
||||
Wheat Crops |
||||
Soil |
||||
Furnace |
||||
Sign Post |
||||
Wooden Door |
||||
Ladder |
||||
Rails |
||||
Cobblestone Stairs |
||||
Wall Sign |
||||
Lever |
||||
Stone Pressure Plate |
||||
Iron Door Block |
||||
Wooden Pressure Plate |
||||
Redstone Ore |
||||
Redstone Torch |
||||
Stone Button |
||||
Snow |
||||
Ice |
||||
Snow Block |
||||
Cactus |
||||
Clay |
||||
Sugar Cane |
||||
Jukebox |
||||
Fence |
||||
Pumpkin |
||||
Netherrack |
||||
Soul Sand |
||||
Glowstone |
||||
Portal |
||||
Jack-O-Lantern |
||||
Cake Block |
||||
Redstone Repeater Block |
||||
Locked Chest |
||||
Trapdoor |
||||
Stone (Silverfish) |
||||
Stone Brick |
||||
Mossy Stone Brick |
||||
Cracked Stone Brick |
||||
Red Mushroom Cap |
||||
Brown Mushroom Cap |
||||
Iron Bars |
||||
Glass Pane |
||||
Melon Block |
||||
Pumpkin Stem |
||||
Melon Stem |
||||
Vines |
||||
Fence Gate |
||||
Brick Stairs |
||||
Stone Brick Stairs |
||||
Mycelium |
||||
Lily Pad |
||||
Nether Brick |
||||
Nether Brick Fence |
||||
Nether Brick Stairs |
||||
Nether Wart |
||||
Iron Shovel |
||||
Iron Pickaxe |
||||
Iron Axe |
||||
Flint and Steel |
||||
Apple |
||||
Bow |
||||
Arrow |
||||
Coal |
||||
Charcoal |
||||
Diamond |
||||
Iron Ingot |
||||
Gold Ingot |
||||
Iron Sword |
||||
Wooden Sword |
||||
Wooden Shovel |
||||
Wooden Pickaxe |
||||
Wooden Axe |
||||
Stone Sword |
||||
Stone Shovel |
||||
Stone Pickaxe |
||||
Stone Axe |
||||
Diamond Sword |
||||
Diamond Shovel |
||||
Diamond Pickaxe |
||||
Diamond Axe |
||||
Stick |
||||
Bowl |
||||
Mushroom Soup |
||||
Gold Sword |
||||
Gold Shovel |
||||
Gold Pickaxe |
||||
Gold Axe |
||||
String |
||||
Feather |
||||
Sulphur |
||||
Wooden Hoe |
||||
Stone Hoe |
||||
Iron Hoe |
||||
Diamond Hoe |
||||
Gold Hoe |
||||
Wheat Seeds |
||||
Wheat |
||||
Bread |
||||
Leather Helmet |
||||
Leather Chestplate |
||||
Leather Leggings |
||||
Leather Boots |
||||
Chainmail Helmet |
||||
Chainmail Chestplate |
||||
Chainmail Leggings |
||||
Chainmail Boots |
||||
Iron Helmet |
||||
Iron Chestplate |
||||
Iron Leggings |
||||
Iron Boots |
||||
Diamond Helmet |
||||
Diamond Chestplate |
||||
Diamond Leggings |
||||
Diamond Boots |
||||
Gold Helmet |
||||
Gold Chestplate |
||||
Gold Leggings |
||||
Gold Boots |
||||
Flint |
||||
Raw Porkchop |
||||
Cooked Porkchop |
||||
Painting |
||||
Golden Apple |
||||
Sign |
||||
Wooden Door |
||||
Bucket |
||||
Water Bucket |
||||
Lava Bucket |
||||
Minecart |
||||
Saddle |
||||
Iron Door |
||||
Redstone |
||||
Snowball |
||||
Boat |
||||
Leather |
||||
Milk Bucket |
||||
Clay Brick |
||||
Clay Balls |
||||
Sugarcane |
||||
Paper |
||||
Book |
||||
Slimeball |
||||
Storage Minecart |
||||
Powered Minecart |
||||
Egg |
||||
Compass |
||||
Fishing Rod |
||||
Clock |
||||
Glowstone Dust |
||||
Raw Fish |
||||
Cooked Fish |
||||
Ink Sack |
||||
Rose Red |
||||
Cactus Green |
||||
Coco Beans |
||||
Lapis Lazuli |
||||
Purple Dye |
||||
Cyan Dye |
||||
Light Gray Dye |
||||
Gray Dye |
||||
Pink Dye |
||||
Lime Dye |
||||
Dandelion Yellow |
||||
Light Blue Dye |
||||
Magenta Dye |
||||
Orange Dye |
||||
Bone Meal |
||||
Bone |
||||
Sugar |
||||
Cake |
||||
Bed |
||||
Redstone Repeater |
||||
Cookie |
||||
Map |
||||
Shears |
||||
Melon |
||||
Pumpkin Seeds |
||||
Melon Seeds |
||||
Raw Beef |
||||
Steak |
||||
Raw Chicken |
||||
Cooked Chicken |
||||
Rotten Flesh |
||||
Ender Pearl |
||||
Blaze Rod |
||||
Ghast Tear |
||||
Gold Nugget |
||||
Nether Wart Seeds |
||||
Potion |
||||
Glass Bottle |
||||
Spider Eye |
||||
Fermented Spider Eye |
||||
Blaze Powder |
||||
Magma Cream |
||||
Gold Music Disc |
||||
Green Music Disc |
||||
Chicken |
||||
Cow |
||||
Mooshroom |
||||
Ocelot |
||||
Pig |
||||
Sheep |
||||
Squid |
||||
Villager |
||||
Enderman |
||||
Wolf |
||||
Zombie Pigman |
||||
Wolf |
||||
Ocelot |
||||
Blaze |
||||
Cave Spider |
||||
Creeper |
||||
Ghast |
||||
Magma Cube |
||||
Silverfish |
||||
Skeleton |
||||
Slime |
||||
Spider |
||||
Spider Jockey |
||||
Zombie |
||||
Snow Golem |
||||
Iron Golem |
||||
Ender Dragon |
||||
Rana |
@ -0,0 +1,13 @@
|
||||
bot: |
||||
datadir: /home/example/bot/data/ |
||||
rpcbind: 0.0.0.0 |
||||
rpcport: 1876 |
||||
connection: |
||||
server: irc.freenode.net |
||||
ipv6: off |
||||
port: 6667 |
||||
modules: |
||||
- PingResponder |
||||
- Services |
||||
- MySQL |
||||
- AttributeStorage |
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python |
||||
|
||||
import logging |
||||
import os |
||||
import yaml |
||||
|
||||
class ModuleBase: |
||||
" All modules must extend this class. " |
||||
def __init__(self, bot, moduleName): |
||||
" Module name is passed from the actual module " |
||||
self.moduleName=moduleName |
||||
" Reference to the bot is saved" |
||||
self.bot = bot |
||||
" Hooks are provided requested by the actual module " |
||||
self.hooks=[] |
||||
" Services provided by the actual module " |
||||
self.services=[] |
||||
" Config is blank until the Module calls loadConfig " |
||||
self.config={} |
||||
" Set up logging for this module " |
||||
self.log = logging.getLogger("Module.%s" % self.moduleName) |
||||
self.log.info("Loaded module %s" % self.moduleName) |
||||
|
||||
|
||||
def loadConfig(self): |
||||
configPath = self.bot.getConfigPath(self.moduleName) |
||||
|
||||
if os.path.exists( configPath ): |
||||
self.config = yaml.load(open(configPath, 'r')) |
||||
|
||||
def ondisable(self): |
||||
pass |
||||
|
||||
def getConfigPath(self): |
||||
return self.bot.getConfigPath(self.moduleName) |
||||
|
||||
def getFilePath(self, f=None): |
||||
return self.bot.getDataPath(self.moduleName) + (f if f else '') |
||||
|
||||
class ModuleHook: |
||||
def __init__(self, hook, method): |
||||
self.hook=hook |
||||
self.method=method |
@ -0,0 +1,454 @@
|
||||
#!/usr/bin/env python |
||||
import socket |
||||
import asynchat |
||||
import logging |
||||
import traceback |
||||
import time |
||||
import sys |
||||
from socket import SHUT_RDWR |
||||
from core.rpc import BotRPC |
||||
|
||||
try: |
||||
from cStringIO import StringIO |
||||
except: |
||||
from io import BytesIO as StringIO |
||||
|
||||
class PyIRCBot(asynchat.async_chat): |
||||
def __init__(self, coreconfig, botconfig): |
||||
asynchat.async_chat.__init__(self) |
||||
" logging " |
||||
self.log = logging.getLogger('PyIRCBot') |
||||
" config " |
||||
self.coreconfig = coreconfig |
||||
self.botconfig = botconfig |
||||
" rpc " |
||||
self.rpc = BotRPC(self) |
||||
|
||||
" stringio object as buffer " |
||||
self.buffer = StringIO() |
||||
" line terminator " |
||||
self.set_terminator(b"\r\n") |
||||
|
||||
" Setup hooks for modules " |
||||
self.initHooks() |
||||
" Load modules " |
||||
self.initModules() |
||||
|
||||
self._connect() |
||||
self.connected=False |
||||
|
||||
def kill(self): |
||||
" Close RPC Socket " |
||||
#try: |
||||
# self.rpc.server._Server__transport.shutdown(SHUT_RDWR) |
||||
#except Exception as e: |
||||
# self.log.error(str(e)) |
||||
try: |
||||
self.rpc.server._Server__transport.close() |
||||
except Exception as e: |
||||
self.log.error(str(e)) |
||||
|
||||
" Kill RPC thread " |
||||
self.rpc._stop() |
||||
|
||||
" Close all modules " |
||||
self.closeAllModules() |
||||
|
||||
" Net related code " |
||||
|
||||
def getBuf(self): |
||||
" return buffer and clear " |
||||
self.buffer.seek(0) |
||||
data = self.buffer.read() |
||||
self.buffer = StringIO() |
||||
return data |
||||
|
||||
def collect_incoming_data(self, data): |
||||
" Recieve data from stream, add to buffer " |
||||
self.log.debug("<< %(message)s", {"message":repr(data)}) |
||||
self.buffer.write(data) |
||||
|
||||
def found_terminator(self): |
||||
" A complete command was pushed through, so clear the buffer and process it." |
||||
self.process_data(self.getBuf().decode("UTF-8")) |
||||
|
||||
def handle_close(self): |
||||
" called on socket shutdown " |
||||
self.log.debug("handle_close") |
||||
self.connected=False |
||||
self.close() |
||||
|
||||
self.log.warning("Connection was lost.") |
||||
|
||||
#self.log.warning("Connection was lost. Reconnecting in 5 seconds.") |
||||
#time.sleep(5) |
||||
#self._connect() |
||||
|
||||
def handle_error(self, *args, **kwargs): |
||||
raise |
||||
|
||||
def _connect(self): |
||||
self.log.debug("Connecting to %(server)s:%(port)i", {"server":self.botconfig["connection"]["server"], "port":self.botconfig["connection"]["port"]}) |
||||
socket_type = socket.AF_INET |
||||
if self.botconfig["connection"]["ipv6"]: |
||||
self.log.info("IPv6 is enabled.") |
||||
socket_type = socket.AF_INET6 |
||||
socketInfo = socket.getaddrinfo(self.botconfig["connection"]["server"], self.botconfig["connection"]["port"], socket_type) |
||||
self.create_socket(socket_type, socket.SOCK_STREAM) |
||||
if "bindaddr" in self.botconfig["connection"]: |
||||
self.bind((self.botconfig["connection"]["bindaddr"], 0)) |
||||
self.connect(socketInfo[0][4]) |
||||
|
||||
def handle_connect(self): |
||||
" Called when the first packets come through, so we ident here " |
||||
self.connected=True |
||||
self.log.debug("handle_connect: setting USER and NICK") |
||||
self.fire_hook("_CONNECT") |
||||
self.log.debug("handle_connect: complete") |
||||
|
||||
def sendRaw(self, text): |
||||
if self.connected: |
||||
self.log.debug(">> "+text) |
||||
self.send( (text+"\r\n").encode("ascii")) |
||||
else: |
||||
self.log.warning("Send attempted while disconnected. >> "+text) |
||||
|
||||
def process_data(self, data): |
||||
" called per line of irc sent through " |
||||
if data.strip() == "": |
||||
return |
||||
|
||||
prefix = None |
||||
command = None |
||||
args=[] |
||||
trailing=None |
||||
|
||||
if data[0]==":": |
||||
prefix=data.split(" ")[0][1:] |
||||
data=data[data.find(" ")+1:] |
||||
command = data.split(" ")[0] |
||||
data=data[data.find(" ")+1:] |
||||
if(data[0]==":"): |
||||
# no args |
||||
trailing = data[1:].strip() |
||||
else: |
||||
trailing = data[data.find(" :")+2:].strip() |
||||
data = data[:data.find(" :")] |
||||
args = data.split(" ") |
||||
for index,arg in enumerate(args): |
||||
args[index]=arg.strip() |
||||
if not command in self.hookcalls: |
||||
self.log.warning("Unknown command: cmd='%s' prefix='%s' args='%s' trailing='%s'" % (command, prefix, args, trailing)) |
||||
else: |
||||
self.fire_hook(command, args=args, prefix=prefix, trailing=trailing) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
" Module related code " |
||||
def initHooks(self): |
||||
self.hooks = [ |
||||
'_CONNECT', # Called when the bot connects to IRC on the socket level |
||||
'NOTICE', # :irc.129irc.com NOTICE AUTH :*** Looking up your hostname... |
||||
'MODE', # :CloneABCD MODE CloneABCD :+iwx |
||||
'PING', # PING :irc.129irc.com |
||||
'JOIN', # :CloneA!dave@hidden-B4F6B1AA.rit.edu JOIN :#clonea |
||||
'QUIT', # :HCSMPBot!~HCSMPBot@108.170.48.18 QUIT :Quit: Disconnecting! |
||||
'NICK', # :foxiAway!foxi@irc.hcsmp.com NICK :foxi |
||||
'PART', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PART #clonea |
||||
'PRIVMSG', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PRIVMSG #clonea :aaa |
||||
'KICK', # :xMopxShell!~rduser@host KICK #xMopx2 xBotxShellTest :xBotxShellTest |
||||
'INVITE', # :gmx!~gmxgeek@irc.hcsmp.com INVITE Tyrone :#hcsmp' |
||||
'001', # :irc.129irc.com 001 CloneABCD :Welcome to the 129irc IRC Network CloneABCD!CloneABCD@djptwc-laptop1.rit.edu |
||||
'002', # :irc.129irc.com 002 CloneABCD :Your host is irc.129irc.com, running version Unreal3.2.8.1 |
||||
'003', # :irc.129irc.com 003 CloneABCD :This server was created Mon Jul 19 2010 at 03:12:01 EDT |
||||
'004', # :irc.129irc.com 004 CloneABCD irc.129irc.com Unreal3.2.8.1 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj |
||||
'005', # :irc.129irc.com 005 CloneABCD CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server |
||||
'250', # :chaos.esper.net 250 xBotxShellTest :Highest connection count: 1633 (1632 clients) (186588 connections received) |
||||
'251', # :irc.129irc.com 251 CloneABCD :There are 1 users and 48 invisible on 2 servers |
||||
'252', # :irc.129irc.com 252 CloneABCD 9 :operator(s) online |
||||
'254', # :irc.129irc.com 254 CloneABCD 6 :channels formed |
||||
'255', # :irc.129irc.com 255 CloneABCD :I have 42 clients and 1 servers |
||||
'265', # :irc.129irc.com 265 CloneABCD :Current Local Users: 42 Max: 47 |
||||
'266', # :irc.129irc.com 266 CloneABCD :Current Global Users: 49 Max: 53 |
||||
'332', # :chaos.esper.net 332 xBotxShellTest #xMopx2 :/ #XMOPX2 / https://code.google.com/p/pyircbot/ (Channel Topic) |
||||
'333', # :chaos.esper.net 333 xBotxShellTest #xMopx2 xMopxShell!~rduser@108.170.60.242 1344370109 |
||||
'353', # :irc.129irc.com 353 CloneABCD = #clonea :CloneABCD CloneABC |
||||
'366', # :irc.129irc.com 366 CloneABCD #clonea :End of /NAMES list. |
||||
'372', # :chaos.esper.net 372 xBotxShell :motd text here |
||||
'375', # :chaos.esper.net 375 xBotxShellTest :- chaos.esper.net Message of the Day - |
||||
'376', # :chaos.esper.net 376 xBotxShell :End of /MOTD command. |
||||
'422', # :irc.129irc.com 422 CloneABCD :MOTD File is missing |
||||
'433', # :nova.esper.net 433 * pyircbot3 :Nickname is already in use. |
||||
] |
||||
" mapping of hooks to methods " |
||||
self.hookcalls = {} |
||||
for command in self.hooks: |
||||
self.hookcalls[command]=[] |
||||
|
||||
def fire_hook(self, command, args=None, prefix=None, trailing=None): |
||||
for hook in self.hookcalls[command]: |
||||
try: |
||||
hook(args, prefix, trailing) |
||||
except: |
||||
self.log.warning("Error processing hook: \n%s"% self.trace()) |
||||
|
||||
def initModules(self): |
||||
" load modules specified in config " |
||||
" storage of imported modules " |
||||
self.modules = {} |
||||
" instances of modules " |
||||
self.moduleInstances = {} |
||||
" append module location to path " |
||||
sys.path.append(self.coreconfig["moduledir"]) |
||||
" append bot directory to path " |
||||
sys.path.append(self.coreconfig["botdir"]+"core/") |
||||
|
||||
for modulename in self.botconfig["modules"]: |
||||
self.loadmodule(modulename) |
||||
|
||||
def importmodule(self, name): |
||||
" import a module by name " |
||||
" check if already exists " |
||||
if not name in self.modules: |
||||
" attempt to load " |
||||
try: |
||||
moduleref = __import__(name) |
||||
self.modules[name]=moduleref |
||||
return (True, None) |
||||
except Exception as e: |
||||
" on failure (usually syntax error in Module code) print an error " |
||||
self.log.error("Module %s failed to load: " % name) |
||||
self.log.error("Module load failure reason: " + str(e)) |
||||
return (False, str(e)) |
||||
else: |
||||
self.log.warning("Module %s already imported" % name) |
||||
return (False, "Module already imported") |
||||
|
||||
def deportmodule(self, name): |
||||
" remove a module from memory by name " |
||||
" unload if necessary " |
||||
if name in self.moduleInstances: |
||||
self.unloadmodule(name) |
||||
" delete all references to the module" |
||||
if name in self.modules: |
||||
item = self.modules[name] |
||||
del self.modules[name] |
||||
del item |
||||
" delete copy that python stores in sys.modules " |
||||
if name in sys.modules: |
||||
del sys.modules[name] |
||||
|
||||
def loadmodule(self, name): |
||||
" load a module and activate it " |
||||
" check if already loaded " |
||||
if name in self.moduleInstances: |
||||
self.log.warning( "Module %s already loaded" % name ) |
||||
return False |
||||
" check if needs to be imported, and verify it was " |
||||
if not name in self.modules: |
||||
importResult = self.importmodule(name) |
||||
if not importResult[0]: |
||||
return importResult |
||||
" init the module " |
||||
self.moduleInstances[name] = getattr(self.modules[name], name)(self, name) |
||||
" load hooks " |
||||
self.loadModuleHooks(self.moduleInstances[name]) |
||||
|
||||
def unloadmodule(self, name): |
||||
" unload a module by name " |
||||
if name in self.moduleInstances: |
||||
" notify the module of disabling " |
||||
self.moduleInstances[name].ondisable() |
||||
" unload all hooks " |
||||
self.unloadModuleHooks(self.moduleInstances[name]) |
||||
" remove the instance " |
||||
item = self.moduleInstances.pop(name) |
||||
" delete the instance" |
||||
del item |
||||
self.log.info( "Module %s unloaded" % name ) |
||||
return (True, None) |
||||
else: |
||||
self.log.info("Module %s not loaded" % name) |
||||
return (False, "Module not loaded") |
||||
|
||||
def reloadmodule(self, name): |
||||
" unload then load a module by name " |
||||
" make sure it's imporeted" |
||||
if name in self.modules: |
||||
" remember if it was loaded before" |
||||
loadedbefore = name in self.moduleInstances |
||||
self.log.info("Reloading %s" % self.modules[name]) |
||||
" unload " |
||||
self.unloadmodule(name) |
||||
" load " |
||||
if loadedbefore: |
||||
self.loadmodule(name) |
||||
return (True, None) |
||||
return (False, "Module is not loaded") |
||||
|
||||
def redomodule(self, name): |
||||
" reload a modules code from disk " |
||||
" remember if it was loaded before " |
||||
loadedbefore = name in self.moduleInstances |
||||
" unload/deport " |
||||
self.deportmodule(name) |
||||
" import " |
||||
importResult = self.importmodule(name) |
||||
if not importResult[0]: |
||||
return importResult |
||||
" load " |
||||
if loadedbefore: |
||||
self.loadmodule(name) |
||||
return (True, None) |
||||
|
||||
def loadModuleHooks(self, module): |
||||
" activate a module's hooks " |
||||
for hook in module.hooks: |
||||
self.addHook(hook.hook, hook.method) |
||||
|
||||
def unloadModuleHooks(self, module): |
||||
" remove a modules hooks " |
||||
for hook in module.hooks: |
||||
self.removeHook(hook.hook, hook.method) |
||||
|
||||
def addHook(self, command, method): |
||||
" add a single hook " |
||||
if command in self.hooks: |
||||
self.hookcalls[command].append(method) |
||||
else: |
||||
self.log.warning("Invalid hook - %s" % command) |
||||
return False |
||||
|
||||
def removeHook(self, command, method): |
||||
" remove a single hook " |
||||
if command in self.hooks: |
||||
for hookedMethod in self.hookcalls[command]: |
||||
if hookedMethod == method: |
||||
self.hookcalls[command].remove(hookedMethod) |
||||
else: |
||||
self.log.warning("Invalid hook - %s" % command) |
||||
return False |
||||
|
||||
def getmodulebyname(self, name): |
||||
" return a module specified by the name " |
||||
if not name in self.moduleInstances: |
||||
return None |
||||
return self.moduleInstances[name] |
||||
|
||||
def getmodulesbyservice(self, service): |
||||
" get a list of modules that provide the specified service " |
||||
validModules = [] |
||||
for module in self.moduleInstances: |
||||
if service in self.moduleInstances[module].services: |
||||
validModules.append(self.moduleInstances[module]) |
||||
return validModules |
||||
|
||||
def getBestModuleForService(self, service): |
||||
m = self.getmodulesbyservice(service) |
||||
if len(m)>0: |
||||
return m[0] |
||||
return None |
||||
|
||||
def closeAllModules(self): |
||||
" Deport all modules (for shutdown). Modules are unloaded in the opposite order listed in the config. " |
||||
loaded = list(self.moduleInstances.keys()) |
||||
loadOrder = self.botconfig["modules"] |
||||
loadOrder.reverse() |
||||
for key in loadOrder: |
||||
if key in loaded: |
||||
loaded.remove(key) |
||||
self.deportmodule(key) |
||||
for key in loaded: |
||||
self.deportmodule(key) |
||||
|
||||
" Filesystem Methods " |
||||
def getDataPath(self, moduleName): |
||||
return "%s/data/%s/" % (self.botconfig["bot"]["datadir"], moduleName) |
||||
|
||||
def getConfigPath(self, moduleName): |
||||
return "%s/config/%s.yml" % (self.botconfig["bot"]["datadir"], moduleName) |
||||
|
||||
" Utility methods " |
||||
@staticmethod |
||||
def decodePrefix(prefix): |
||||
" Returns an object with nick, username, hostname attributes" |
||||
if "!" in prefix: |
||||
ob = type('UserPrefix', (object,), {}) |
||||
ob.nick, prefix = prefix.split("!") |
||||
ob.username, ob.hostname = prefix.split("@") |
||||
return ob |
||||
else: |
||||
ob = type('ServerPrefix', (object,), {}) |
||||
ob.hostname = prefix |
||||
return ob |
||||
|
||||
@staticmethod |
||||
def trace(): |
||||
return traceback.format_exc() |
||||
|
||||
@staticmethod |
||||
def messageHasCommand(command, message, requireArgs=False): |
||||
# Check if the message at least starts with the command |
||||
messageBeginning = message[0:len(command)] |
||||
if messageBeginning!=command: |
||||
return False |
||||
# Make sure it's not a subset of a longer command (ie .meme being set off by .memes) |
||||
subsetCheck = message[len(command):len(command)+1] |
||||
if subsetCheck!=" " and subsetCheck!="": |
||||
return False |
||||
|
||||
# We've got the command! Do we need args? |
||||
argsStart = len(command) |
||||
args = "" |
||||
if argsStart > 0: |
||||
args = message[argsStart+1:] |
||||
|
||||
if requireArgs and args.strip() == '': |
||||
return False |
||||
|
||||
# Verified! Return the set. |
||||
ob = type('ParsedCommand', (object,), {}) |
||||
ob.command = command |
||||
ob.args = [] if args=="" else args.split(" ") |
||||
ob.args_str = args |
||||
ob.message = message |
||||
return ob |
||||
# return (True, command, args, message) |
||||
|
||||
|
||||
" Data Methods " |
||||
def get_nick(self): |
||||
return self.config["nick"] |
||||
|
||||
|
||||
" Action Methods " |
||||
def act_PONG(self, data): |
||||
self.sendRaw("PONG :%s" % data) |
||||
|
||||
def act_USER(self, username, hostname, realname): |
||||
self.sendRaw("USER %s %s %s :%s" % (username, hostname, self.botconfig["connection"]["server"], realname)) |
||||
|
||||
def act_NICK(self, newNick): |
||||
self.sendRaw("NICK %s" % newNick) |
||||
|
||||
def act_JOIN(self, channel): |
||||
self.sendRaw("JOIN %s"%channel) |
||||
|
||||
def act_PRIVMSG(self, towho, message): |
||||
self.sendRaw("PRIVMSG %s :%s"%(towho,message)) |
||||
|
||||
def act_MODE(self, channel, mode, extra=None): |
||||
if extra != None: |
||||
self.sendRaw("MODE %s %s %s" % (channel,mode,extra)) |
||||
else: |
||||
self.sendRaw("MODE %s %s" % (channel,mode)) |
||||
|
||||
def act_ACTION(self, channel, action): |
||||
self.sendRaw("PRIVMSG %s :\x01ACTION %s"%(channel,action)) |
||||
|
||||
def act_KICK(self, channel, who, comment): |
||||
self.sendRaw("KICK %s %s :%s" % (channel, who, comment)) |
||||
|
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python |
||||
import traceback |
||||
import logging |
||||
from core import jsonrpc |
||||
from threading import Thread |
||||
|
||||
class BotRPC(Thread): |
||||
def __init__(self, main): |
||||
Thread.__init__(self) |
||||
self.bot = main |
||||
self.log = logging.getLogger('RPC') |
||||
self.server = jsonrpc.Server(jsonrpc.JsonRpc20(), jsonrpc.TransportTcpIp(addr=(self.bot.botconfig["bot"]["rpcbind"], self.bot.botconfig["bot"]["rpcport"]))) |
||||
|
||||
self.server.register_function( self.importModule ) |
||||
self.server.register_function( self.deportModule ) |
||||
self.server.register_function( self.loadModule ) |
||||
self.server.register_function( self.unloadModule ) |
||||
self.server.register_function( self.reloadModule ) |
||||
self.server.register_function( self.redoModule ) |
||||
self.server.register_function( self.getTraceback ) |
||||
self.start() |
||||
|
||||
def run(self): |
||||
self.server.serve() |
||||
|
||||
|
||||
def importModule(self, moduleName): |
||||
return self.bot.importmodule(moduleName) |
||||
|
||||
def deportModule(self, moduleName): |
||||
self.bot.deportmodule(moduleName) |
||||
|
||||
def loadModule(self, moduleName): |
||||
return self.bot.loadmodule(moduleName) |
||||
|
||||
def unloadModule(self, moduleName): |
||||
self.bot.unloadmodule(moduleName) |
||||
|
||||
def reloadModule(self, moduleName): |
||||
self.bot.unloadmodule(moduleName) |
||||
return self.bot.loadmodule(moduleName) |
||||
|
||||
def redoModule(self, moduleName): |
||||
return self.bot.redomodule(moduleName) |
||||
|
||||
def getTraceback(self): |
||||
tb = str(traceback.format_exc()) |
||||
print(tb) |
||||
return tb |
||||
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import sys |
||||
import logging |
||||
import yaml |
||||
import asyncore |
||||
from optparse import OptionParser |
||||
from core.pyircbot import PyIRCBot |
||||
|
||||
if __name__ == "__main__": |
||||
" logging level and facility " |
||||
logging.basicConfig(level=logging.DEBUG, format="%(asctime)-15s %(levelname)-8s %(message)s") |
||||
log = logging.getLogger('main') |
||||
|
||||
" parse command line args " |
||||
parser = OptionParser() |
||||
parser.add_option("-c", "--config", action="store", type="string", dest="config", help="Path to core config file") |
||||
parser.add_option("-b", "--bot", action="store", type="string", dest="bot", help="Path to bot config file") |
||||
|
||||
(options, args) = parser.parse_args() |
||||
|
||||
log.debug(options) |
||||
|
||||
if not options.config: |
||||
log.critical("No core config file specified (-c). Exiting.") |
||||
sys.exit(0) |
||||
if not options.bot: |
||||
log.critical("No bot config file specified (-b). Exiting.") |
||||
sys.exit(0) |
||||
|
||||
coreconfig = yaml.load(open(options.config, 'r')) |
||||
botconfig = yaml.load(open(options.bot, 'r')) |
||||
|
||||
log.debug(coreconfig) |
||||
log.debug(botconfig) |
||||
|
||||
bot = PyIRCBot(coreconfig, botconfig) |
||||
try: |
||||
asyncore.loop() |
||||
except KeyboardInterrupt: |
||||
bot.kill() |
||||
|
@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python |
||||
from modulebase import ModuleBase,ModuleHook |
||||
|
||||
class AttributeStorage(ModuleBase): |
||||
def __init__(self, bot, moduleName): |
||||
ModuleBase.__init__(self, bot, moduleName); |
||||
self.hooks=[] |
||||
self.services=["attributes"] |
||||
self.db = None |
||||
serviceProviders = self.bot.getmodulesbyservice("mysql") |
||||
if len(serviceProviders)==0: |
||||
self.log.error("AttributeStorage: Could not find a valid mysql service provider") |
||||
else: |
||||
self.log.info("AttributeStorage: Selecting mysql service provider: %s" % serviceProviders[0]) |
||||
self.db = serviceProviders[0] |
||||
|
||||
if not self.db.connection.tableExists("attribute"): |
||||
self.log.info("AttributeStorage: Creating table: attribute") |
||||
c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `attribute` ( |
||||
`id` int(11) NOT NULL AUTO_INCREMENT, |
||||
`attribute` varchar(128) NOT NULL, |
||||
PRIMARY KEY (`id`), |
||||
UNIQUE KEY `attribute` (`attribute`) |
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") |
||||
c.close() |
||||
|
||||
if not self.db.connection.tableExists("items"): |
||||
self.log.info("AttributeStorage: Creating table: items") |
||||
c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `items` ( |
||||
`id` int(11) NOT NULL AUTO_INCREMENT, |
||||
`item` varchar(512) CHARACTER SET utf8 NOT NULL, |
||||
PRIMARY KEY (`id`) |
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") |
||||
c.close() |
||||
|
||||
if not self.db.connection.tableExists("values"): |
||||
self.log.info("AttributeStorage: Creating table: values") |
||||
c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `values` ( |
||||
`itemid` int(11) NOT NULL, |
||||
`attributeid` int(11) NOT NULL, |
||||
`value` varchar(512) CHARACTER SET utf8 NOT NULL, |
||||
PRIMARY KEY (`itemid`,`attributeid`) |
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") |
||||
c.close() |
||||
|
||||
# self.getItem('xMopxShell', 'name') |
||||
# self.getAttribute('xMopxShell', 'name') |
||||
# self.setAttribute('xMopxShell', 'name', 'dave') |
||||
|
||||
|
||||
def getItem(self, name): |
||||
c = self.db.connection.query("""SELECT |
||||
`i`.`id`, |
||||
`i`.`item`, |
||||
`a`.`attribute`, |
||||
`v`.`value` |
||||
FROM |
||||
`items` `i` |
||||
INNER JOIN `values` `v` |
||||
on `v`.`itemid`=`i`.`id` |
||||
INNER JOIN `attribute` `a` |
||||
on `a`.`id`=`v`.`attributeid` |
||||
|
||||
WHERE |
||||
`i`.`item`=%s;""", |
||||
(name,) |
||||
) |
||||
item = {} |
||||
while True: |
||||
row = c.fetchone() |
||||
if row == None: |
||||
break |
||||
item[row["attribute"]]=row["value"] |
||||
c.close() |
||||
|
||||
if len(item)==0: |
||||
return {} |
||||
return item |
||||
|
||||
def getAttribute(self, item, attribute): |
||||
c = self.db.connection.query("""SELECT |
||||
`i`.`id`, |
||||
`i`.`item`, |
||||
`a`.`attribute`, |
||||
`v`.`value` |
||||
FROM |
||||
`items` `i` |
||||
INNER JOIN `values` `v` |
||||
on `v`.`itemid`=`i`.`id` |
||||
INNER JOIN `attribute` `a` |
||||
on `a`.`id`=`v`.`attributeid` |
||||
|
||||
WHERE |
||||
`i`.`item`=%s |
||||
AND |
||||
`a`.`attribute`=%s;""", |
||||
(item,attribute) |
||||
) |
||||
row = c.fetchone() |
||||
c.close() |
||||
if row == None: |
||||
return None |
||||
return row["value"] |
||||
|
||||
def setAttribute(self, item, attribute, value): |
||||
item = item.lower() |
||||
attribute = attribute.lower() |
||||
|
||||
# Check attribute exists |
||||
c = self.db.connection.query("SELECT `id` FROM `attribute` WHERE `attribute`=%s;", (attribute)) |
||||
row = c.fetchone() |
||||
attributeId = -1 |
||||
if row == None: |
||||
c = self.db.connection.query("INSERT INTO `attribute` (`attribute`) VALUES (%s);", (attribute)) |
||||
attributeId = c.lastrowid |
||||
else: |
||||
attributeId = row["id"] |
||||
c.close() |
||||
|
||||
# check item exists |
||||
c = self.db.connection.query("SELECT `id` FROM `items` WHERE `item`=%s;", (item)) |
||||
row = c.fetchone() |
||||
itemId = -1 |
||||
if row == None: |
||||
c = self.db.connection.query("INSERT INTO `items` (`item`) VALUES (%s);", (item)) |
||||
itemId = c.lastrowid |
||||
else: |
||||
itemId = row["id"] |
||||
c.close() |
||||
|
||||
if value == None: |
||||
# delete it |
||||
c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId)) |
||||
self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) |
||||
else: |
||||
# add attribute |
||||
c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (%s, %s, %s);", (itemId, attributeId, value)) |
||||
self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) |
||||
c.close() |
@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env python |
||||
from modulebase import ModuleBase,ModuleHook |
||||
import random |
||||
import yaml |
||||
import os |
||||
import time |
||||
import math |
||||
import hashlib |
||||
from threading import Timer |
||||
|
||||
class DogeDice(ModuleBase): |
||||
def __init__(self, bot, moduleName): |
||||
ModuleBase.__init__(self, bot, moduleName); |
||||
self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] |
||||
self.loadConfig() |
||||
# Load attribute storage |
||||
self.attr = self.bot.getBestModuleForService("attributes") |
||||
# Load doge RPC |
||||
self.doge = self.bot.getBestModuleForService("dogerpc") |
||||
# Dict of #channel -> game object |
||||
self.games = {} |
||||
|
||||
def gotMsg(self, args, prefix, trailing): |
||||
prefixObj = self.bot.decodePrefix(prefix) |
||||
# Ignore messages from users not logged in |
||||
loggedinfrom = self.attr.getAttribute(prefixObj.nick, "loggedinfrom") |
||||
if loggedinfrom==None: |
||||
# Send them a hint? |
||||
return |
||||
elif prefixObj.hostname == loggedinfrom: |
||||
if args[0][0] == "#": |
||||
# create a blank game obj if there isn't one (and whitelisted ? ) |
||||
if not args[0] in self.games and (not self.config["channelWhitelistOn"] or (self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"]) ): |
||||
self.games[args[0]]=gameObj(self, args[0]) |
||||
# Channel message |
||||
self.games[args[0]].gotMsg(args, prefix, trailing) |
||||
else: |
||||
# Private message |
||||
#self.games[args[0].gotPrivMsg(args, prefix, trailing) |
||||
pass |
||||
else: |
||||
# Ignore potential spoofing |
||||
pass |
||||
|
||||
def removeGame(self, channel): |
||||
del self.games[channel] |
||||
|
||||
def ondisable(self): |
||||
self.log.info("DogeDice: Unload requested, ending games...") |
||||
while len(self.games)>0: |
||||
first = list(self.games.keys())[0] |
||||
self.games[first].gameover() |
||||
|
||||
class gameObj: |
||||
def __init__(self, master, channel): |
||||
self.master = master |
||||
self.channel = channel |
||||
# Game state |
||||
# 0 = waiting for players |
||||
# - advertise self? |
||||
# - players must be registered and have enough doge for current bet |
||||
# 1 = enough players, countdown |
||||
# - Last warning to pull out |
||||
# 2 = locked in / game setup |
||||
# - Move doge from player's wallets to table wallet. kick players if they can't afford |
||||
# 3 = start of a round |
||||
# - Each player's turn to roll |
||||
# 4 = determine winner, move doge |
||||
# - if > 10 doge, house fee? |
||||
self.step = 0 |
||||
|
||||
# Bet amount |
||||
self.bet = 0.0 |
||||
# players list |
||||
self.players = [] |
||||
# min players |
||||
self.minPlayers = 2 |
||||
# max players |
||||
self.maxPlayers = 4 |
||||
# Lobby countdown timer |
||||
self.startCountdownTimer = None |
||||
# pre-result timer |
||||
self.endgameResultTimer = None |
||||
# in-game timeout |
||||
self.playTimeout = None |
||||
# Wallet for this game |
||||
self.walletName = None |
||||
|
||||
def getPlayer(self, nick): |
||||
for player in self.players: |
||||
if player.nick == nick: |
||||
return player |
||||
return None |
||||
|
||||
def gotPrivMsg(self, args, prefix, trailing): |
||||
prefix = self.master.bot.decodePrefix(prefix) |
||||
pass |
||||
|
||||
def gotMsg(self, args, prefix, trailing): |
||||
prefix = self.master.bot.decodePrefix(prefix) |
||||
if self.step == 0 or self.step == 1: |
||||
# Join game |
||||
cmd = self.master.bot.messageHasCommand(".join", trailing) |
||||
if cmd: |
||||
if len(self.players)-1 < self.maxPlayers: |
||||
if self.getPlayer(prefix.nick)==None: |
||||
userWallet = self.master.attr.getAttribute(prefix.nick, "dogeaccountname") |
||||
if userWallet == None: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) |
||||
return |
||||
balance = self.master.doge.getBal(userWallet) |
||||
|
||||
# check if the room is 'opened' already: |
||||
if len(self.players)==0: |
||||
# require an amount |
||||
if len(cmd.args)==1: |
||||
# Check if they have enough coins |
||||
try: |
||||
bet = float(cmd.args[0]) |
||||
except: |
||||
return |
||||
|
||||
if bet < self.master.config["minBet"]: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: Minimum bet is %s DOGE!" % (prefix.nick, self.master.config["minBet"])) |
||||
return |
||||
|
||||
if balance>=bet: |
||||
newPlayer = playerObj(self, prefix.nick) |
||||
newPlayer.dogeWalletName = userWallet |
||||
self.players.append(newPlayer) |
||||
self.bet = bet |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) |
||||
else: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) |
||||
else: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You need to specify a bet amount: .join 10" % (prefix.nick)) |
||||
else: |
||||
# no amount required |
||||
if balance>=self.bet: |
||||
newPlayer = playerObj(self, prefix.nick) |
||||
newPlayer.dogeWalletName = userWallet |
||||
self.players.append(newPlayer) |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) |
||||
if self.canStart() and self.startCountdownTimer == None: |
||||
self.initStartCountdown() |
||||
self.master.bot.act_PRIVMSG(self.channel, "The game will start in %s seconds! Bet is %s DOGE each!" % (self.master.config["lobbyIdleSeconds"], self.bet)) |
||||
else: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) |
||||
|
||||
else: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: you're already in the game. Quit with .leave" % (prefix.nick)) |
||||
else: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: the game is full (%s/%)! Cannot join." % (prefix.nick, len(self.players), self.maxPlayers)) |
||||
# Leave game |
||||
cmd = self.master.bot.messageHasCommand(".leave", trailing) |
||||
if cmd: |
||||
if self.getPlayer(prefix.nick)==None: |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You're not in the game." % (prefix.nick)) |
||||
else: |
||||
self.removePlayer(prefix.nick) |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s: You have left the game!" % (prefix.nick)) |
||||
if not self.canStart() and self.startCountdownTimer: |
||||
self.clearTimer(self.startCountdownTimer) |
||||
self.startCountdownTimer = None |
||||
self.master.bot.act_PRIVMSG(self.channel, "Game start aborted." ) |
||||
self.step = 0 |
||||
elif self.step == 2: |
||||
pass |
||||
elif self.step == 3: |
||||
# Ignore cmds from people outside the game |
||||
player = self.getPlayer(prefix.nick) |
||||
if not player: |
||||
return |
||||
|
||||
# handle a .roll |
||||
cmd = self.master.bot.messageHasCommand(".roll", trailing) |
||||
if cmd and not player.hasRolled: |
||||
roll1 = random.randint(1,6) |
||||
roll2 = random.randint(1,6) |
||||
self.master.bot.act_PRIVMSG(self.channel, "%s rolls %s and %s!" % (prefix.nick, roll1, roll2)) |
||||
player.hasRolled = True |
||||
player.rollValue = roll1+roll2 |
||||
|
||||
# Check if all players have rolled |
||||
for player in self.players: |
||||
if not player.hasRolled: |
||||
return |
||||
|
||||
# start endgame timer |
||||
self.step = 4 |
||||
self.endgameResultTimer = Timer(2, self.endgameResults) |
||||
self.endgameResultTimer.start() |
||||
|
||||
elif self.step == 4: |
||||
pass |
||||
|
||||
#senderIsOp = self.master.attr.getAttribute(prefix.nick, "op")=="yes" |
||||
def clearTimer(self, timer): |
||||
if timer: |
||||
timer.cancel() |
||||
timer = None |
||||
|
||||
def removePlayer(self, playerNick): |
||||
pos = -1 |
||||
for i in range(0, len(self.players)): |
||||
if self.players[i].nick == playerNick: |
||||
pos = i |
||||
break |
||||
if pos >= 0: |
||||
self.players.pop(pos) |
||||
|
||||
def canStart(self): |
||||
# Return true if the step is 'lobby' mode and player count is OK |
||||
return self.step == 0 and len(self.players)>=self.minPlayers |
||||
def initStartCountdown(self): |
||||
# Start the game-start countdown |
||||
self.startCountdownTimer = Timer(self.master.config["lobbyIdleSeconds"], self.lobbyCountdownDone) |
||||
self.startCountdownTimer.start() |
||||
self.step = 1 |
||||
|
||||
def lobbyCountdownDone(self): |
||||
self.step = 2 |
||||