Initial commit
This commit is contained in:
commit
83d30ceab6
2
config.main.yml
Normal file
2
config.main.yml
Normal file
@ -0,0 +1,2 @@
|
||||
botdir: /home/example/bot/pyircbot/
|
||||
moduledir: /home/example/bot/pyircbot/modules/
|
5
data/config/DogeDice.yml
Normal file
5
data/config/DogeDice.yml
Normal file
@ -0,0 +1,5 @@
|
||||
minBet: .01
|
||||
lobbyIdleSeconds: 15
|
||||
channelWhitelistOn: True
|
||||
channelWhitelist:
|
||||
- test
|
4
data/config/DogeRPC.yml
Normal file
4
data/config/DogeRPC.yml
Normal file
@ -0,0 +1,4 @@
|
||||
host: 127.0.0.1
|
||||
username: root
|
||||
password: root
|
||||
port: 22555
|
7
data/config/DogeScramble.yml
Normal file
7
data/config/DogeScramble.yml
Normal file
@ -0,0 +1,7 @@
|
||||
hintDelay: 15
|
||||
delayNext: 5
|
||||
maxHints: 5
|
||||
abortAfterNoGuesses: 2
|
||||
winAmount: 0.1
|
||||
categoryduration: 2
|
||||
decreaseFactor: 0.83
|
3
data/config/GameBase.yml
Normal file
3
data/config/GameBase.yml
Normal file
@ -0,0 +1,3 @@
|
||||
channelWhitelistOn: True
|
||||
channelWhitelist:
|
||||
- test
|
4
data/config/MySQL.yml
Normal file
4
data/config/MySQL.yml
Normal file
@ -0,0 +1,4 @@
|
||||
host: 127.0.0.1
|
||||
username: root
|
||||
password: root
|
||||
database: pyircbot
|
5
data/config/Scramble.yml
Normal file
5
data/config/Scramble.yml
Normal file
@ -0,0 +1,5 @@
|
||||
hintDelay: 15
|
||||
delayNext: 5
|
||||
maxHints: 5
|
||||
abortAfterNoGuesses: 5
|
||||
decreaseFactor: 0.83
|
23
data/config/Services.yml
Normal file
23
data/config/Services.yml
Normal file
@ -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"
|
50
data/data/DogeScramble/us_states.txt
Normal file
50
data/data/DogeScramble/us_states.txt
Normal file
@ -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
|
313
data/data/Scramble/words.txt
Normal file
313
data/data/Scramble/words.txt
Normal file
@ -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
|
13
pyircbot.yml
Normal file
13
pyircbot.yml
Normal file
@ -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
pyircbot/core/__init__.py
Normal file
0
pyircbot/core/__init__.py
Normal file
1113
pyircbot/core/jsonrpc.py
Normal file
1113
pyircbot/core/jsonrpc.py
Normal file
File diff suppressed because it is too large
Load Diff
43
pyircbot/core/modulebase.py
Normal file
43
pyircbot/core/modulebase.py
Normal file
@ -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
|
454
pyircbot/core/pyircbot.py
Normal file
454
pyircbot/core/pyircbot.py
Normal file
@ -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))
|
||||
|
52
pyircbot/core/rpc.py
Normal file
52
pyircbot/core/rpc.py
Normal file
@ -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
|
||||
|
||||
|
||||
|
42
pyircbot/main.py
Normal file
42
pyircbot/main.py
Normal file
@ -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()
|
||||
|
139
pyircbot/modules/AttributeStorage.py
Normal file
139
pyircbot/modules/AttributeStorage.py
Normal file
@ -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()
|
315
pyircbot/modules/DogeDice.py
Normal file
315
pyircbot/modules/DogeDice.py
Normal file
@ -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
|
||||
self.master.bot.act_PRIVMSG(self.channel, "Collecting DOGE and starting game.. Type .roll !")
|
||||
# Make a wallet for this game
|
||||
self.walletName = "DogeDice-"+self.channel
|
||||
# Generate an address to 'create' a wallet
|
||||
self.master.doge.getAcctAddr(self.walletName)
|
||||
|
||||
# Verify and move funds from each player
|
||||
for player in self.players:
|
||||
playerBalance = self.master.doge.getAcctBal(player.dogeWalletName)
|
||||
if playerBalance < self.bet:
|
||||
self.master.bot.act_PRIVMSG(self.channel, "%s was dropped from the game!")
|
||||
self.removePlayer(player.nick)
|
||||
|
||||
if len(self.players) <= 1:
|
||||
self.master.bot.act_PRIVMSG(self.channel, "1 or players left - game over!")
|
||||
self.resetGame()
|
||||
return
|
||||
|
||||
# Take doges
|
||||
for player in self.players:
|
||||
self.master.doge.move(player.dogeWalletName, self.walletName, self.bet)
|
||||
|
||||
# Pre-game setup (nothing !)
|
||||
|
||||
# Accept game commands
|
||||
self.step = 3
|
||||
|
||||
# Start play timeout
|
||||
self.playTimeout = Timer(30, self.gamePlayTimeoutExpired)
|
||||
self.playTimeout.start()
|
||||
|
||||
def gamePlayTimeoutExpired(self):
|
||||
# Time out - return doges
|
||||
self.master.bot.act_PRIVMSG(self.channel, "Time expired! Returning all doges.")
|
||||
if self.step == 3:
|
||||
# In game step. Refund doges
|
||||
for player in self.players:
|
||||
self.master.doge.move(self.walletName, player.dogeWalletName, self.bet)
|
||||
self.resetGame()
|
||||
|
||||
def endgameResults(self):
|
||||
maxRollNames = []
|
||||
maxRollValue = 0
|
||||
for player in self.players:
|
||||
if player.rollValue > maxRollValue:
|
||||
maxRollNames = []
|
||||
maxRollNames.append(player.nick)
|
||||
maxRollValue = player.rollValue
|
||||
if player.rollValue == maxRollValue:
|
||||
if not player.nick in maxRollNames:
|
||||
maxRollNames.append(player.nick)
|
||||
|
||||
pot = self.master.doge.getAcctBal(self.walletName)
|
||||
DOGEeachDec = pot/len(maxRollNames)
|
||||
DOGEeach = math.floor(DOGEeachDec*100000000) / 100000000
|
||||
|
||||
if len(maxRollNames)==1:
|
||||
self.master.bot.act_PRIVMSG(self.channel, "We have a winner - %s! Winnings are: %s DOGE" % (maxRollNames[0], DOGEeach))
|
||||
else:
|
||||
self.master.bot.act_PRIVMSG(self.channel, "We have a tie between %s - The take is %s DOGE each" % (' and '.join(maxRollNames), DOGEeach))
|
||||
|
||||
# Pay out
|
||||
for nick in maxRollNames:
|
||||
player = self.getPlayer(nick)
|
||||
self.master.doge.move(self.walletName, player.dogeWalletName, DOGEeach)
|
||||
|
||||
# the end!
|
||||
self.resetGame(< |