Initial commit
This commit is contained in:
commit
83d30ceab6
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
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()
|
||||||
|
|
||||||
|
def resetGame(self):
|
||||||
|
self.clearTimer(self.startCountdownTimer)
|
||||||
|
self.startCountdownTimer = None
|
||||||
|
self.clearTimer(self.endgameResultTimer)
|
||||||
|
self.endgameResultTimer = None
|
||||||
|
self.clearTimer(self.playTimeout)
|
||||||
|
self.playTimeout = None
|
||||||
|
self.master.removeGame(self.channel)
|
||||||
|
|
||||||
|
def gameover(self):
|
||||||
|
self.gamePlayTimeoutExpired()
|
||||||
|
|
||||||
|
|
||||||
|
class playerObj:
|
||||||
|
def __init__(self, game, nick):
|
||||||
|
self.game = game
|
||||||
|
self.nick = nick
|
||||||
|
# Save the player's wallet name
|
||||||
|
self.dogeWalletName = None
|
||||||
|
# Set to true after they roll
|
||||||
|
self.hasRolled = False
|
||||||
|
# Sum of their dice
|
||||||
|
self.rollValue = None
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
from bitcoinrpc.authproxy import AuthServiceProxy
|
||||||
|
|
||||||
|
class DogeRPC(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[]
|
||||||
|
self.services=["dogerpc"]
|
||||||
|
self.loadConfig()
|
||||||
|
self.rpc = DogeController(self)
|
||||||
|
|
||||||
|
def getBal(self, acct):
|
||||||
|
" get a balance of an address or an account "
|
||||||
|
return self.getAcctBal(acct)
|
||||||
|
|
||||||
|
def getAcctAddr(self, acct):
|
||||||
|
" returns the address for an account. creates if necessary "
|
||||||
|
self.rpc.ping()
|
||||||
|
addrs = self.rpc.con.getaddressesbyaccount(acct)
|
||||||
|
if len(addrs)==0:
|
||||||
|
return self.rpc.con.getnewaddress(acct)
|
||||||
|
return addrs[0]
|
||||||
|
|
||||||
|
def getAcctBal(self, acct):
|
||||||
|
" returns an account's balance"
|
||||||
|
self.rpc.ping()
|
||||||
|
return float(self.rpc.con.getbalance(acct))
|
||||||
|
|
||||||
|
def canMove(self, fromAcct, toAcct, amount):
|
||||||
|
" true or false if fromAcct can afford to give toAcct an amount of coins "
|
||||||
|
balfrom = self.getAcctBal(fromAcct)
|
||||||
|
return balfrom >= amount
|
||||||
|
|
||||||
|
def move(self, fromAcct, toAcct, amount):
|
||||||
|
" move coins from one account to another "
|
||||||
|
self.rpc.ping()
|
||||||
|
if self.canMove(fromAcct, toAcct, amount):
|
||||||
|
return self.rpc.con.move(fromAcct, toAcct, amount)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send(self, fromAcct, toAddr, amount):
|
||||||
|
" send coins to an external addr "
|
||||||
|
self.rpc.ping()
|
||||||
|
if self.canMove(fromAcct, toAddr, amount):
|
||||||
|
return self.rpc.con.sendfrom(fromAcct, toAddr, amount)
|
||||||
|
return False
|
||||||
|
|
||||||
|
class DogeController:
|
||||||
|
def __init__(self, master):
|
||||||
|
self.config = master.config
|
||||||
|
self.log = master.log
|
||||||
|
self.con = None
|
||||||
|
self.ping()
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
try:
|
||||||
|
self.con.getinfo()
|
||||||
|
except:
|
||||||
|
self.connect()
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
self.log.debug("DogeRPC: Connecting to dogecoind")
|
||||||
|
self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.config["username"], self.config["password"], self.config["host"], self.config["port"]))
|
||||||
|
self.con.getinfo()
|
||||||
|
self.log.debug("DogeRPC: Connected to %s:%s" % (self.config["host"], self.config["port"]))
|
|
@ -0,0 +1,258 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
import random
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from threading import Timer
|
||||||
|
|
||||||
|
class DogeScramble(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[ModuleHook("PRIVMSG", self.scramble)]
|
||||||
|
self.loadConfig()
|
||||||
|
|
||||||
|
# Load attribute storage
|
||||||
|
self.attr = None
|
||||||
|
serviceProviders = self.bot.getmodulesbyservice("attributes")
|
||||||
|
if len(serviceProviders)==0:
|
||||||
|
self.log.error("DogeScramble: Could not find a valid attributes service provider")
|
||||||
|
else:
|
||||||
|
self.log.info("DogeScramble: Selecting attributes service provider: %s" % serviceProviders[0])
|
||||||
|
self.attr = serviceProviders[0]
|
||||||
|
|
||||||
|
# Load doge RPC
|
||||||
|
self.doge = self.bot.getBestModuleForService("dogerpc")
|
||||||
|
|
||||||
|
# Per channel games
|
||||||
|
self.games = {}
|
||||||
|
|
||||||
|
def scramble(self, args, prefix, trailing):
|
||||||
|
channel = args[0]
|
||||||
|
if channel[0] == "#":
|
||||||
|
# Ignore messages from users without a dogewallet password
|
||||||
|
prefixObj = self.bot.decodePrefix(prefix)
|
||||||
|
if self.attr.getAttribute(prefixObj.nick, "password")==None:
|
||||||
|
return
|
||||||
|
if not channel in self.games:
|
||||||
|
self.games[channel]=scrambleGame(self, channel)
|
||||||
|
self.games[channel].scramble(args, prefix, trailing)
|
||||||
|
def ondisable(self):
|
||||||
|
self.log.info("DogeScramble: Unload requested, ending games...")
|
||||||
|
for game in self.games:
|
||||||
|
self.games[game].gameover()
|
||||||
|
|
||||||
|
class scrambleGame:
|
||||||
|
def __init__(self, master, channel):
|
||||||
|
self.master = master
|
||||||
|
self.channel = channel
|
||||||
|
# Running?
|
||||||
|
self.running = False
|
||||||
|
# Current word
|
||||||
|
self.currentWord = None
|
||||||
|
# Current word, scrambled
|
||||||
|
self.scrambled = None
|
||||||
|
# Online?
|
||||||
|
self.scrambleOn = False
|
||||||
|
# Count down to hints
|
||||||
|
self.hintTimer = None
|
||||||
|
# of hints given
|
||||||
|
self.hintsGiven = 0
|
||||||
|
# Cooldown between words
|
||||||
|
self.nextTimer = None
|
||||||
|
# How many guesses submitted this round
|
||||||
|
self.guesses = 0;
|
||||||
|
# How many games in a row where nobody guessed
|
||||||
|
self.gamesWithoutGuesses = 0;
|
||||||
|
# What file are we using
|
||||||
|
self.category_file = None;
|
||||||
|
# How many words in this category have been used?
|
||||||
|
self.category_count = 0
|
||||||
|
# How long between categories
|
||||||
|
self.change_category_after_words = self.master.config["categoryduration"]
|
||||||
|
# Should we change categories at the next pick?
|
||||||
|
self.should_change_category = True
|
||||||
|
# Holds the processed category name
|
||||||
|
self.category_name = None
|
||||||
|
# list of last picked words
|
||||||
|
self.lastwords = []
|
||||||
|
# name of last winner for decreasing return
|
||||||
|
self.lastwinner = None
|
||||||
|
self.lastwinvalue = 0
|
||||||
|
|
||||||
|
self.delayHint = self.master.config["hintDelay"];
|
||||||
|
self.delayNext = self.master.config["delayNext"];
|
||||||
|
self.maxHints = self.master.config["maxHints"];
|
||||||
|
self.abortAfterNoGuesses = self.master.config["abortAfterNoGuesses"];
|
||||||
|
|
||||||
|
def gameover(self):
|
||||||
|
self.clearTimers();
|
||||||
|
self.running = False
|
||||||
|
def clearTimers(self):
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
def clearTimer(self, timer):
|
||||||
|
if timer:
|
||||||
|
timer.cancel()
|
||||||
|
def scramble(self, args, prefix, trailing):
|
||||||
|
prefix = self.master.bot.decodePrefix(prefix)
|
||||||
|
sender = prefix.nick
|
||||||
|
|
||||||
|
senderIsOp = self.master.attr.getAttribute(prefix.nick, "op")=="yes"
|
||||||
|
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scramble", trailing)
|
||||||
|
if cmd and not self.running:
|
||||||
|
#and senderIsOp
|
||||||
|
self.running = True
|
||||||
|
self.startScramble()
|
||||||
|
return
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scrambleoff", trailing)
|
||||||
|
if cmd and senderIsOp and self.running:
|
||||||
|
self.gameover()
|
||||||
|
self.running = False
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.currentWord and trailing.strip().lower() == self.currentWord:
|
||||||
|
# Get winner withdraw address
|
||||||
|
useraddr = self.master.attr.getAttribute(prefix.nick, "dogeaddr")
|
||||||
|
userwallet = self.master.attr.getAttribute(prefix.nick, "dogeaccountname")
|
||||||
|
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s got the word - %s!" % (sender, self.currentWord))
|
||||||
|
|
||||||
|
if not useraddr:
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s: to win DOGE, you must set an wallet address by PMing me \".setdogeaddr\". Next word in %s seconds." % (prefix.nick, self.delayNext))
|
||||||
|
else:
|
||||||
|
winamount = float(self.master.config["winAmount"])
|
||||||
|
if self.lastwinner == prefix.nick:
|
||||||
|
winamount = self.lastwinvalue * self.master.config["decreaseFactor"]
|
||||||
|
self.lastwinvalue = winamount
|
||||||
|
self.lastwinner = prefix.nick
|
||||||
|
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s won %s DOGE! Next word in %s seconds." % (prefix.nick, round(winamount, 8), self.delayNext))
|
||||||
|
self.master.doge.move('', userwallet, winamount)
|
||||||
|
|
||||||
|
self.currentWord = None
|
||||||
|
self.clearTimers()
|
||||||
|
self.hintsGiven = 0
|
||||||
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
self.guesses=0
|
||||||
|
self.category_count+=1
|
||||||
|
self.master.log.debug("DogeScramble: category_count is: %s" % (self.category_count))
|
||||||
|
if self.category_count >= self.change_category_after_words:
|
||||||
|
self.should_change_category = True
|
||||||
|
else:
|
||||||
|
self.guesses+=1
|
||||||
|
|
||||||
|
def startScramble(self):
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
self.nextTimer = Timer(0, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
|
||||||
|
def startNewWord(self):
|
||||||
|
self.currentWord = self.pickWord()
|
||||||
|
self.scrambled = self.scrambleWord(self.currentWord)
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "[Category: %s] Unscramble this: %s " % (self.category_name, self.scrambled))
|
||||||
|
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
||||||
|
self.hintTimer.start()
|
||||||
|
|
||||||
|
def giveHint(self):
|
||||||
|
self.hintsGiven+=1
|
||||||
|
|
||||||
|
if self.hintsGiven>=len(self.currentWord) or self.hintsGiven > self.maxHints:
|
||||||
|
self.abortWord()
|
||||||
|
return
|
||||||
|
|
||||||
|
blanks = ""
|
||||||
|
for letter in list(self.currentWord):
|
||||||
|
if letter == " ":
|
||||||
|
blanks+=" "
|
||||||
|
else:
|
||||||
|
blanks+="_"
|
||||||
|
partFromWord = self.currentWord[0:self.hintsGiven]
|
||||||
|
partFromBlanks = blanks[self.hintsGiven:]
|
||||||
|
hintstr = partFromWord+partFromBlanks
|
||||||
|
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr))
|
||||||
|
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
||||||
|
self.hintTimer.start()
|
||||||
|
|
||||||
|
def abortWord(self):
|
||||||
|
cur = self.currentWord
|
||||||
|
self.currentWord = None
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was '%s'. Next word in %s seconds." % (cur, self.delayNext))
|
||||||
|
self.hintsGiven = 0
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
|
||||||
|
if self.guesses==0:
|
||||||
|
self.gamesWithoutGuesses+=1
|
||||||
|
if self.gamesWithoutGuesses >= self.abortAfterNoGuesses:
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scramble to start again.")
|
||||||
|
self.gameover()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.gamesWithoutGuesses=0
|
||||||
|
|
||||||
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
|
||||||
|
def catFileNameToStr(self, s):
|
||||||
|
s=s.split(".")[0]
|
||||||
|
s=s.replace("_", " ")
|
||||||
|
return s.title()
|
||||||
|
|
||||||
|
def pickWord(self):
|
||||||
|
if self.should_change_category:
|
||||||
|
# clear flags
|
||||||
|
self.should_change_category = False
|
||||||
|
self.category_count = 0
|
||||||
|
# Get the path to word files dir
|
||||||
|
dirpath = self.master.getFilePath("")
|
||||||
|
# List dir
|
||||||
|
files = os.listdir(dirpath)
|
||||||
|
# choose a random file
|
||||||
|
random.shuffle(files)
|
||||||
|
self.category_file = files[0]
|
||||||
|
self.category_name = self.catFileNameToStr(self.category_file)
|
||||||
|
# Process the name & announce
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "The category is now: %s " % self.category_name)
|
||||||
|
# count lines
|
||||||
|
f = open(self.master.getFilePath(self.category_file), "r")
|
||||||
|
lines = 0
|
||||||
|
while True:
|
||||||
|
lines+=1
|
||||||
|
if f.readline() == "":
|
||||||
|
break
|
||||||
|
f.close()
|
||||||
|
# change category
|
||||||
|
picked = ""
|
||||||
|
while picked == "" or picked in self.lastwords:
|
||||||
|
|
||||||
|
skip = random.randint(0, lines)
|
||||||
|
f = open(self.master.getFilePath(self.category_file), "r")
|
||||||
|
while skip>=0:
|
||||||
|
f.readline()
|
||||||
|
skip-=1
|
||||||
|
picked = f.readline().strip().lower()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
self.master.log.debug("DogeScramble: picked %s for %s" % (picked, self.channel))
|
||||||
|
self.lastwords.append(picked)
|
||||||
|
if len(self.lastwords) > 5:
|
||||||
|
self.lastwords.pop(0)
|
||||||
|
return picked
|
||||||
|
|
||||||
|
def scrambleWord(self, word):
|
||||||
|
scrambled = ""
|
||||||
|
for subword in word.split(" "):
|
||||||
|
scrambled+=self.scrambleIndividualWord(subword)+ " "
|
||||||
|
return scrambled.strip()
|
||||||
|
|
||||||
|
def scrambleIndividualWord(self, word):
|
||||||
|
scrambled = list(word)
|
||||||
|
random.shuffle(scrambled)
|
||||||
|
return ''.join(scrambled).lower()
|
|
@ -0,0 +1,164 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
import time
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
class DogeWallet(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)]
|
||||||
|
# Load attribute storage
|
||||||
|
self.attr = self.bot.getBestModuleForService("attributes")
|
||||||
|
# Load doge RPC
|
||||||
|
self.doge = self.bot.getBestModuleForService("dogerpc")
|
||||||
|
|
||||||
|
def gotmsg(self, args, prefix, trailing):
|
||||||
|
channel = args[0]
|
||||||
|
if channel[0] == "#":
|
||||||
|
# Ignore channel messages
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.handlePm(args, prefix, trailing)
|
||||||
|
|
||||||
|
def handlePm(self, args, prefix, trailing):
|
||||||
|
prefix = self.bot.decodePrefix(prefix)
|
||||||
|
cmd = self.bot.messageHasCommand(".setpass", trailing)
|
||||||
|
if cmd:
|
||||||
|
if len(cmd.args)==0:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or \".setpass oldpass newpass\"")
|
||||||
|
else:
|
||||||
|
oldpass = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
if oldpass == None:
|
||||||
|
self.attr.setAttribute(prefix.nick, "password", cmd.args[0])
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[0])
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==2:
|
||||||
|
if cmd.args[0] == oldpass:
|
||||||
|
self.attr.setAttribute(prefix.nick, "password", cmd.args[1])
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[1])
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: Old password incorrect.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: You must provide the old password when setting a new one.")
|
||||||
|
cmd = self.bot.messageHasCommand(".setdogeaddr", trailing)
|
||||||
|
if cmd:
|
||||||
|
userpw = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
if userpw==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: You must first set a password with .setpass")
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==2:
|
||||||
|
if userpw == cmd.args[0]:
|
||||||
|
self.attr.setAttribute(prefix.nick, "dogeaddr", cmd.args[1])
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: Your doge address has been set to \"%s\"." % cmd.args[1])
|
||||||
|
# if they don't have a wallet name, we'll make one now
|
||||||
|
if self.attr.getAttribute(prefix.nick, "dogeaccountname")==None:
|
||||||
|
randName = self.md5(str(time.time()))[0:10]
|
||||||
|
self.attr.setAttribute(prefix.nick, "dogeaccountname", randName)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: incorrect password.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: usage: \".setdogeaddr password address\" or \".setdogeaddr mypassword D8VNy3zkMGspffcFSWWqsxx7GrtVsmF2up\"")
|
||||||
|
|
||||||
|
cmd = self.bot.messageHasCommand(".getdogebal", trailing)
|
||||||
|
if cmd:
|
||||||
|
userpw = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
if userpw==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: You must first set a password with .setpass")
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==1:
|
||||||
|
if userpw == cmd.args[0]:
|
||||||
|
#################
|
||||||
|
walletname = self.attr.getAttribute(prefix.nick, "dogeaccountname")
|
||||||
|
amount = 0.0
|
||||||
|
if walletname:
|
||||||
|
amount = self.doge.getBal(walletname)
|
||||||
|
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: Your balance is: %s DOGE" % amount)
|
||||||
|
|
||||||
|
#################
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: incorrect password.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: usage: \".getdogebal password\"")
|
||||||
|
|
||||||
|
cmd = self.bot.messageHasCommand(".withdrawdoge", trailing)
|
||||||
|
if cmd:
|
||||||
|
userpw = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
useraddr = self.attr.getAttribute(prefix.nick, "dogeaddr")
|
||||||
|
if userpw==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a password with .setpass")
|
||||||
|
elif useraddr==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a withdraw address .setdogeaddr")
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==2:
|
||||||
|
if userpw == cmd.args[0]:
|
||||||
|
#################
|
||||||
|
walletname = self.attr.getAttribute(prefix.nick, "dogeaccountname")
|
||||||
|
walletbal = self.doge.getBal(walletname)
|
||||||
|
|
||||||
|
desiredAmount = float(cmd.args[1])
|
||||||
|
|
||||||
|
if walletbal >= desiredAmount:
|
||||||
|
txn = self.doge.send(walletname, useraddr, desiredAmount)
|
||||||
|
if txn:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: %s DOGE sent to %s. Transaction ID: %s"% (desiredAmount, useraddr, txn))
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: Unable to create transaction. Please contact an Operator.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You only have %s DOGE. You cannot withdraw %s DOGE." % (walletbal, desiredAmount))
|
||||||
|
#################
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: incorrect password.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: usage: \".withdrawdoge password amount\" - \".withdrawdoge mypassword 5.0\" - ")
|
||||||
|
|
||||||
|
cmd = self.bot.messageHasCommand(".getdepositaddr", trailing)
|
||||||
|
if cmd:
|
||||||
|
userpw = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
if userpw==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: You must first set a password with .setpass")
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==1:
|
||||||
|
if userpw == cmd.args[0]:
|
||||||
|
#################
|
||||||
|
walletname = self.attr.getAttribute(prefix.nick, "dogeaccountname")
|
||||||
|
addr = self.doge.getAcctAddr(walletname)
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: Your deposit address is: %s" % addr)
|
||||||
|
#################
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: incorrect password.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: usage: \".getdepositaddr password\"")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmd = self.bot.messageHasCommand(".login", trailing)
|
||||||
|
if cmd:
|
||||||
|
userpw = self.attr.getAttribute(prefix.nick, "password")
|
||||||
|
if userpw==None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass")
|
||||||
|
else:
|
||||||
|
if len(cmd.args)==1:
|
||||||
|
if userpw == cmd.args[0]:
|
||||||
|
#################
|
||||||
|
self.attr.setAttribute(prefix.nick, "loggedinfrom", prefix.hostname)
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".login: You have been logged in from: %s" % prefix.hostname)
|
||||||
|
#################
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.")
|
||||||
|
else:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"")
|
||||||
|
cmd = self.bot.messageHasCommand(".logout", trailing)
|
||||||
|
if cmd:
|
||||||
|
loggedin = self.attr.getAttribute(prefix.nick, "loggedinfrom")
|
||||||
|
if loggedin == None:
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".logout: You must first be logged in")
|
||||||
|
else:
|
||||||
|
self.attr.setAttribute(prefix.nick, "loggedinfrom", None)
|
||||||
|
self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.")
|
||||||
|
|
||||||
|
def md5(self, data):
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(data.encode("ascii"))
|
||||||
|
return m.hexdigest()
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
import random
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from threading import Timer
|
||||||
|
|
||||||
|
class GameBase(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
|
||||||
|
if self.attr.getAttribute(prefixObj.nick, "loggedinfrom")==None:
|
||||||
|
# Send them a hint?
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
def ondisable(self):
|
||||||
|
self.log.info("GameBase: Unload requested, ending games...")
|
||||||
|
for game in self.games:
|
||||||
|
self.games[game].gameover()
|
||||||
|
|
||||||
|
class gameObj:
|
||||||
|
def __init__(self, master, channel):
|
||||||
|
self.master = master
|
||||||
|
self.channel = channel
|
||||||
|
|
||||||
|
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)
|
||||||
|
pass
|
||||||
|
|
||||||
|
#senderIsOp = self.master.attr.getAttribute(prefix.nick, "op")=="yes"
|
||||||
|
def gameover(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class playerObj:
|
||||||
|
def __init__(self, game, nick):
|
||||||
|
self.game = game
|
||||||
|
self.nick = nick
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import MySQLdb #python 2.x
|
||||||
|
except:
|
||||||
|
import pymysql as MySQLdb #python 3.x
|
||||||
|
|
||||||
|
class MySQL(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[]
|
||||||
|
self.services=["mysql"]
|
||||||
|
self.loadConfig()
|
||||||
|
self.connection = self.getConnection()
|
||||||
|
|
||||||
|
def getConnection(self):
|
||||||
|
return Connection(self)
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self, master):
|
||||||
|
self.config = master.config
|
||||||
|
self.log = master.log
|
||||||
|
self._connect()
|
||||||
|
|
||||||
|
# Check if the table requested exists
|
||||||
|
def tableExists(self, tablename):
|
||||||
|
c = self.getCursor()
|
||||||
|
c.execute("SHOW TABLES;")
|
||||||
|
tables = c.fetchall()
|
||||||
|
if len(tables)==0:
|
||||||
|
return False;
|
||||||
|
key = list(tables[0].keys())[0]
|
||||||
|
for table in tables:
|
||||||
|
if table[key]==tablename:
|
||||||
|
return True;
|
||||||
|
return False
|
||||||
|
|
||||||
|
def query(self, queryText, args=()):
|
||||||
|
c = self.getCursor()
|
||||||
|
if len(args)==0:
|
||||||
|
c.execute(queryText)
|
||||||
|
else:
|
||||||
|
c.execute(queryText, args)
|
||||||
|
return c
|
||||||
|
|
||||||
|
# Returns a cusor object, after checking for connectivity
|
||||||
|
def getCursor(self):
|
||||||
|
self.ensureConnected()
|
||||||
|
if sys.version_info > (3,0):
|
||||||
|
c = self.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
else:
|
||||||
|
c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor)
|
||||||
|
c.execute("USE `%s`;" % self.config["database"])
|
||||||
|
return c
|
||||||
|
|
||||||
|
def escape(self, s):
|
||||||
|
self.ensureConnected()
|
||||||
|
return self.connection.escape_string(s)
|
||||||
|
|
||||||
|
def ensureConnected(self):
|
||||||
|
try:
|
||||||
|
self.connection.ping()
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
self.connection.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
del self.connection
|
||||||
|
self._connect()
|
||||||
|
|
||||||
|
def ondisable(self):
|
||||||
|
self.connection.close()
|
||||||
|
|
||||||
|
# Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet)
|
||||||
|
def _connect(self):
|
||||||
|
self.log.info("MySQL: Connecting to db host at %s" % self.config["host"])
|
||||||
|
self.connection = MySQLdb.connect(host=self.config["host"],user=self.config["username"] ,passwd=self.config["password"])
|
||||||
|
self.log.info("MySQL: Connected.")
|
||||||
|
self.connection.autocommit(True)
|
||||||
|
c = None
|
||||||
|
if sys.version_info > (3,0):
|
||||||
|
c = self.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
else:
|
||||||
|
c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor)
|
||||||
|
|
||||||
|
c.execute("SHOW DATABASES")
|
||||||
|
dblist = c.fetchall()
|
||||||
|
found = False
|
||||||
|
for row in dblist:
|
||||||
|
if row["Database"]==self.config["database"]:
|
||||||
|
found = True
|
||||||
|
if not found:
|
||||||
|
c.execute("CREATE DATABASE `%s`;" % self.config["database"])
|
||||||
|
c.execute("USE `%s`;" % self.config["database"])
|
||||||
|
c.close()
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
|
||||||
|
class PingResponder(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[ModuleHook("PING", self.pingrespond)]
|
||||||
|
def pingrespond(self, args, prefix, trailing):
|
||||||
|
# got a ping? send it right back
|
||||||
|
self.bot.act_PONG(trailing)
|
||||||
|
self.log.info("Responded to a ping: %s" % trailing)
|
|
@ -0,0 +1,232 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
import random
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from threading import Timer
|
||||||
|
from operator import itemgetter
|
||||||
|
|
||||||
|
class Scramble(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
# init the base module
|
||||||
|
ModuleBase.__init__(self, bot, moduleName);
|
||||||
|
self.hooks=[ModuleHook("PRIVMSG", self.scramble)]
|
||||||
|
self.loadConfig()
|
||||||
|
|
||||||
|
# Dictionary
|
||||||
|
self.wordsCount=0;
|
||||||
|
self.wordsFile = self.getFilePath("words.txt")
|
||||||
|
print(self.wordsFile)
|
||||||
|
wordsF = open(self.wordsFile, "r")
|
||||||
|
while True:
|
||||||
|
word = wordsF.readline()
|
||||||
|
if word=="":
|
||||||
|
break
|
||||||
|
self.wordsCount+=1
|
||||||
|
wordsF.close
|
||||||
|
self.log.info("Scramble: Loaded %s words" % str(self.wordsCount))
|
||||||
|
# Load scores
|
||||||
|
self.scoresFile = self.getFilePath("scores.yml")
|
||||||
|
if not os.path.exists(self.scoresFile):
|
||||||
|
yaml.dump({}, file(self.scoresFile, 'w'))
|
||||||
|
self.scores = yaml.load(file(self.scoresFile, 'r'))
|
||||||
|
# Per channel games
|
||||||
|
self.games = {}
|
||||||
|
# Hook in
|
||||||
|
self.hooks=[ModuleBase.ModuleHook("PRIVMSG", self.scramble)]
|
||||||
|
|
||||||
|
def scramble(self, args, prefix, trailing):
|
||||||
|
channel = args[0]
|
||||||
|
if channel[0] == "#":
|
||||||
|
if not channel in self.games:
|
||||||
|
self.games[channel]=scrambleGame(self, channel)
|
||||||
|
self.games[channel].scramble(args, prefix, trailing)
|
||||||
|
|
||||||
|
def saveScores(self):
|
||||||
|
yaml.dump(self.scores, file(self.scoresFile, 'w'))
|
||||||
|
|
||||||
|
def getScore(self, player, add=0):
|
||||||
|
player = player.lower()
|
||||||
|
if not player in self.scores:
|
||||||
|
self.scores[player] = 0
|
||||||
|
if not add == 0:
|
||||||
|
self.scores[player]+=add
|
||||||
|
self.saveScores()
|
||||||
|
|
||||||
|
return self.scores[player]
|
||||||
|
|
||||||
|
def getScoreNoWrite(self, player):
|
||||||
|
if not player.lower() in self.scores:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return self.getScore(player)
|
||||||
|
|
||||||
|
def ondisable(self):
|
||||||
|
self.log.info("Scramble: Unload requested, ending games...")
|
||||||
|
for game in self.games:
|
||||||
|
self.games[game].gameover()
|
||||||
|
self.saveScores()
|
||||||
|
|
||||||
|
class scrambleGame:
|
||||||
|
def __init__(self, master, channel):
|
||||||
|
self.master = master
|
||||||
|
self.channel = channel
|
||||||
|
# Running?
|
||||||
|
self.running = False
|
||||||
|
# Current word
|
||||||
|
self.currentWord = None
|
||||||
|
# Current word, scrambled
|
||||||
|
self.scrambled = None
|
||||||
|
# Online?
|
||||||
|
self.scrambleOn = False
|
||||||
|
# Count down to hints
|
||||||
|
self.hintTimer = None
|
||||||
|
# of hints given
|
||||||
|
self.hintsGiven = 0
|
||||||
|
# Cooldown between words
|
||||||
|
self.nextTimer = None
|
||||||
|
# How many guesses submitted this round
|
||||||
|
self.guesses = 0;
|
||||||
|
# How many games in a row where nobody guessed
|
||||||
|
self.gamesWithoutGuesses = 0;
|
||||||
|
|
||||||
|
self.delayHint = self.master.config["hintDelay"];
|
||||||
|
self.delayNext = self.master.config["delayNext"];
|
||||||
|
self.maxHints = self.master.config["maxHints"];
|
||||||
|
self.abortAfterNoGuesses = self.master.config["abortAfterNoGuesses"];
|
||||||
|
|
||||||
|
def gameover(self):
|
||||||
|
self.clearTimers();
|
||||||
|
self.running = False
|
||||||
|
def clearTimers(self):
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
def clearTimer(self, timer):
|
||||||
|
if timer:
|
||||||
|
timer.cancel()
|
||||||
|
def scramble(self, args, prefix, trailing):
|
||||||
|
prefix = self.master.bot.decodePrefix(prefix)
|
||||||
|
sender = prefix.nick
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scrambleon", trailing)
|
||||||
|
if cmd and not self.running:
|
||||||
|
self.running = True
|
||||||
|
self.startScramble()
|
||||||
|
return
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scrambleoff", trailing)
|
||||||
|
if cmd and self.running:
|
||||||
|
self.gameover()
|
||||||
|
self.running = False
|
||||||
|
return
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scramble top", trailing)
|
||||||
|
if cmd:
|
||||||
|
sortedscores = []
|
||||||
|
for player in self.master.scores:
|
||||||
|
sortedscores.append({'name':player, 'score':self.master.scores[player]})
|
||||||
|
sortedscores = sorted(sortedscores, key=itemgetter('score'))
|
||||||
|
sortedscores.reverse()
|
||||||
|
numScores = len(sortedscores)
|
||||||
|
if numScores>3:
|
||||||
|
numScores=3
|
||||||
|
resp = "Top %s: " % str(numScores)
|
||||||
|
which = 1
|
||||||
|
while which<=numScores:
|
||||||
|
resp+="%s: %s, " % (sortedscores[which-1]["name"], sortedscores[which-1]["score"])
|
||||||
|
which+=1
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, resp[:-2])
|
||||||
|
cmd = self.master.bot.messageHasCommand(".scramble score", trailing)
|
||||||
|
if cmd:
|
||||||
|
someone = cmd.args.strip()
|
||||||
|
if len(someone) > 0:
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s: %s has a score of %s" % (sender, someone, self.master.getScoreNoWrite(someone)))
|
||||||
|
else:
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s: %s" % (sender, self.master.getScore(sender)))
|
||||||
|
if self.currentWord and trailing.strip().lower() == self.currentWord:
|
||||||
|
playerScore = self.master.getScore(sender, 1)
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "%s guessed the word - %s! %s now has %s points. Next word in %s seconds." % (sender, self.currentWord, sender, playerScore, self.delayNext))
|
||||||
|
self.currentWord = None
|
||||||
|
self.clearTimers()
|
||||||
|
self.hintsGiven = 0
|
||||||
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
self.guesses=0
|
||||||
|
else:
|
||||||
|
self.guesses+=1
|
||||||
|
|
||||||
|
def startScramble(self):
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
self.nextTimer = Timer(0, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
|
||||||
|
def startNewWord(self):
|
||||||
|
self.currentWord = self.pickWord()
|
||||||
|
self.master.log.info("Scramble: New word for %s: %s" % (self.channel, self.currentWord))
|
||||||
|
self.scrambled = self.scrambleWord(self.currentWord)
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "New word - %s " % (self.scrambled))
|
||||||
|
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
||||||
|
self.hintTimer.start()
|
||||||
|
|
||||||
|
def giveHint(self):
|
||||||
|
self.hintsGiven+=1
|
||||||
|
|
||||||
|
if self.hintsGiven>=len(self.currentWord) or self.hintsGiven > self.maxHints:
|
||||||
|
self.abortWord()
|
||||||
|
return
|
||||||
|
|
||||||
|
blanks = ""
|
||||||
|
for letter in list(self.currentWord):
|
||||||
|
if letter == " ":
|
||||||
|
blanks+=" "
|
||||||
|
else:
|
||||||
|
blanks+="_"
|
||||||
|
partFromWord = self.currentWord[0:self.hintsGiven]
|
||||||
|
partFromBlanks = blanks[self.hintsGiven:]
|
||||||
|
hintstr = partFromWord+partFromBlanks
|
||||||
|
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr))
|
||||||
|
|
||||||
|
self.clearTimer(self.hintTimer)
|
||||||
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
||||||
|
self.hintTimer.start()
|
||||||
|
|
||||||
|
def abortWord(self):
|
||||||
|
cur = self.currentWord
|
||||||
|
self.currentWord = None
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was %s. Next word in %s seconds." % (cur, self.delayNext))
|
||||||
|
self.hintsGiven = 0
|
||||||
|
self.clearTimer(self.nextTimer)
|
||||||
|
|
||||||
|
if self.guesses==0:
|
||||||
|
self.gamesWithoutGuesses+=1
|
||||||
|
if self.gamesWithoutGuesses >= self.abortAfterNoGuesses:
|
||||||
|
self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scrambleon to start again.")
|
||||||
|
self.gameover()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.gamesWithoutGuesses=0
|
||||||
|
|
||||||
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
||||||
|
self.nextTimer.start()
|
||||||
|
|
||||||
|
def pickWord(self):
|
||||||
|
f = open(self.master.wordsFile, "r")
|
||||||
|
skip = random.randint(0, self.master.wordsCount)
|
||||||
|
while skip>=0:
|
||||||
|
f.readline()
|
||||||
|
skip-=1
|
||||||
|
picked = f.readline().strip().lower()
|
||||||
|
f.close()
|
||||||
|
return picked
|
||||||
|
|
||||||
|
def scrambleWord(self, word):
|
||||||
|
scrambled = ""
|
||||||
|
for subword in word.split(" "):
|
||||||
|
scrambled+=self.scrambleIndividualWord(subword)+ " "
|
||||||
|
return scrambled.strip()
|
||||||
|
|
||||||
|
def scrambleIndividualWord(self, word):
|
||||||
|
scrambled = list(word)
|
||||||
|
random.shuffle(scrambled)
|
||||||
|
return ''.join(scrambled).lower()
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from modulebase import ModuleBase,ModuleHook
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
class Services(ModuleBase):
|
||||||
|
def __init__(self, bot, moduleName):
|
||||||
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
|
self.hooks=[ModuleHook("_CONNECT", self.doConnect), ModuleHook("433", self.nickTaken), ModuleHook("001", self.initservices), ModuleHook("INVITE", self.invited), ]
|
||||||
|
self.loadConfig()
|
||||||
|
self.current_nick = 0
|
||||||
|
self.do_ghost = False
|
||||||
|
def doConnect(self, args, prefix, trailing):
|
||||||
|
self.bot.act_NICK(self.config["user"]["nick"][0])
|
||||||
|
self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"], self.config["user"]["realname"])
|
||||||
|
|
||||||
|
def nickTaken(self, args, prefix, trailing):
|
||||||
|
if self.config["ident"]["ghost"]:
|
||||||
|
self.do_ghost = True
|
||||||
|
self.current_nick+=1
|
||||||
|
if self.current_nick >= len(self.config["user"]["nick"]):
|
||||||
|
self.log.critical("Ran out of usernames while selecting backup username!")
|
||||||
|
return
|
||||||
|
self.bot.act_NICK(self.config["user"]["nick"][self.current_nick])
|
||||||
|
|
||||||
|
def initservices(self, args, prefix, trailing):
|
||||||
|
if self.do_ghost:
|
||||||
|
self.bot.act_PRIVMSG(self.config["ident"]["ghost_to"], self.config["ident"]["ghost_cmd"] % {"nick":self.config["user"]["nick"][0], "password":self.config["user"]["password"]})
|
||||||
|
sleep(2)
|
||||||
|
self.bot.act_NICK(self.config["user"]["nick"][0])
|
||||||
|
self.do_initservices()
|
||||||
|
|
||||||
|
def invited(self, args, prefix, trailing):
|
||||||
|
if trailing.lower() in self.config["privatechannels"]["list"]:
|
||||||
|
self.log.info("Invited to %s, joining" % trailing)
|
||||||
|
self.bot.act_JOIN(trailing)
|
||||||
|
|
||||||
|
def do_initservices(self):
|
||||||
|
" id to nickserv "
|
||||||
|
if self.config["ident"]["enable"]:
|
||||||
|
self.bot.act_PRIVMSG(self.config["ident"]["to"], self.config["ident"]["command"] % {"password":self.config["user"]["password"]})
|
||||||
|
|
||||||
|
" join plain channels "
|
||||||
|
for channel in self.config["channels"]:
|
||||||
|
self.log.info("Joining %s" % channel)
|
||||||
|
self.bot.act_JOIN(channel)
|
||||||
|
|
||||||
|
" request invite for private message channels "
|
||||||
|
for channel in self.config["privatechannels"]["list"]:
|
||||||
|
self.log.info("Requesting invite to %s" % channel)
|
||||||
|
self.bot.act_PRIVMSG(self.config["privatechannels"]["to"], self.config["privatechannels"]["command"] % {"channel":channel})
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
./pyircbot/main.py -c config.main.yml -b pyircbot.yml
|
Loading…
Reference in New Issue