mirror of
https://git.wownero.com/wownero/tippero.git
synced 2024-08-15 00:33:14 +00:00
Add a reddit network
This commit is contained in:
parent
e8954211a3
commit
b1373c629c
2 changed files with 264 additions and 0 deletions
|
@ -52,6 +52,14 @@ network_config = {
|
|||
'timeout_seconds': 600,
|
||||
'channels': ['#txtptest000'],
|
||||
},
|
||||
'reddit': {
|
||||
'subreddits': ['test'],
|
||||
'login': 'testbx',
|
||||
'keyword': '/u/testbx',
|
||||
'user_agent': tipbot_name,
|
||||
'update_period': 90,
|
||||
'load_limit': 100,
|
||||
}
|
||||
}
|
||||
|
||||
dice_min_multiplier=1.1
|
||||
|
|
256
tipbot/modules/reddit.py
Normal file
256
tipbot/modules/reddit.py
Normal file
|
@ -0,0 +1,256 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - Reddit
|
||||
# Copyright 2015 moneromooo
|
||||
#
|
||||
# The Cryptonote tipbot is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import threading
|
||||
import re
|
||||
import praw
|
||||
import tipbot.config as config
|
||||
from tipbot.log import log_error, log_warn, log_info, log_log
|
||||
from tipbot.utils import *
|
||||
from tipbot.command_manager import *
|
||||
from tipbot.network import *
|
||||
|
||||
#import logging
|
||||
#logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
class RedditNetwork(Network):
|
||||
def __init__(self,name):
|
||||
Network.__init__(self,name)
|
||||
self.last_update_time=0
|
||||
self.logged_in=False
|
||||
self.last_seen_ids=None
|
||||
self.thread=None
|
||||
|
||||
def is_identified(self,link):
|
||||
# all reddit users are identified
|
||||
return True
|
||||
|
||||
def connect(self):
|
||||
if self.thread:
|
||||
return False
|
||||
try:
|
||||
cfg=config.network_config[self.name]
|
||||
self.login=cfg['login']
|
||||
password=GetPassword(self.name)
|
||||
self.subreddits=cfg['subreddits']
|
||||
user_agent=cfg['user_agent']
|
||||
self.update_period=cfg['update_period']
|
||||
self.load_limit=cfg['load_limit']
|
||||
self.keyword=cfg['keyword']
|
||||
|
||||
self.reddit=praw.Reddit(user_agent=user_agent)
|
||||
self.reddit.login(self.login,password)
|
||||
self.items_cache=dict()
|
||||
|
||||
self.stop = False
|
||||
self.thread = threading.Thread(target=self.run)
|
||||
self.thread.start()
|
||||
self.logged_in=True
|
||||
|
||||
except Exception,e:
|
||||
log_error('Failed to login to reddit: %s' % str(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def disconnect(self):
|
||||
log_info('Reddit disconnect')
|
||||
if not self.thread:
|
||||
return
|
||||
log_info('Shutting down Reddit thread')
|
||||
self.stop = True
|
||||
self.thread.join()
|
||||
self.thread = None
|
||||
self.items_cache=None
|
||||
self.reddit = None
|
||||
|
||||
def send_group(self,group,msg,data=None):
|
||||
item=data
|
||||
if not item:
|
||||
log_error('RedditNetwork: no item found in send_group: cannot send %s' % (msg))
|
||||
return
|
||||
self._schedule_reply(item,None,msg)
|
||||
|
||||
def send_user(self,user,msg,data=None):
|
||||
item=data
|
||||
if not item:
|
||||
# new PM
|
||||
self._schedule_reply(None,user.nick,msg)
|
||||
else:
|
||||
# reply to PM
|
||||
self._schedule_reply(item,None,msg)
|
||||
|
||||
def is_acceptable_command_prefix(self,s):
|
||||
s=s.strip()
|
||||
if s=="":
|
||||
return True
|
||||
if s.lower() == self.keyword.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
def _parse(self,item,is_pm):
|
||||
author=self.canonicalize(item.author.name) if hasattr(item,'author') else None
|
||||
if author==self.canonicalize(self.login):
|
||||
return
|
||||
|
||||
if item.id in self.last_seen_ids:
|
||||
#log_log('Already seen %s %.1f hours ago by %s: %s (%s), skipping' % (item.id,age/3600,str(author),repr(title),repr(item.body)))
|
||||
return
|
||||
|
||||
age=time.time()-item.created_utc
|
||||
ts=long(float(item.created_utc))
|
||||
title=item.link_title if hasattr(item,'link_title') else None
|
||||
|
||||
log_log('Parsing new item %s from %.1f hours ago by %s: %s (%s)' % (item.id,age/3600,str(author),repr(title),repr(item.body)))
|
||||
self.last_seen_ids.add(item.id)
|
||||
redis_sadd('reddit:last_seen_ids',item.id)
|
||||
|
||||
if item.body.lower().find(self.keyword.lower()) >= 0:
|
||||
group=None
|
||||
#if not is_pm and hasattr(item,'subreddit'):
|
||||
# group=Group(self,item.subreddit.display_name)
|
||||
group = None
|
||||
self.items_cache[item.fullname]=item
|
||||
link=Link(self,User(self,author),group,item)
|
||||
log_info('Found keyword %s from %s' % (self.keyword,link.identity()))
|
||||
for line in item.body.split('\n'):
|
||||
if is_pm:
|
||||
exidx=line.find('!')
|
||||
if exidx!=-1 and len(line)>exidx+1 and line[exidx+1] in string.ascii_letters and self.is_acceptable_command_prefix(line[:exidx]):
|
||||
cmd=line[exidx+1:].split(' ')
|
||||
cmd[0] = cmd[0].strip(' \t\n\r')
|
||||
log_info('Found command from %s: %s' % (link.identity(), str(cmd)))
|
||||
if self.on_command:
|
||||
self.on_command(link,cmd)
|
||||
|
||||
else:
|
||||
# reddit special: +x as a reply means tip
|
||||
if not is_pm and not item.is_root and hasattr(item,'parent_id'):
|
||||
line=line.replace(self.keyword,'').strip()
|
||||
if re.match("\+[0-9]*\.[0-9]*",line):
|
||||
if self.on_command:
|
||||
try:
|
||||
parent_item=self.reddit.get_info(thing_id=item.parent_id)
|
||||
author=parent_item.author.name
|
||||
synthetic_cmd=['tip',author,line.replace('+','')]
|
||||
log_log('Running synthetic command: %s' % (str(synthetic_cmd)))
|
||||
self.on_command(link,synthetic_cmd)
|
||||
except Exception,e:
|
||||
log_error('Failed to tip %s\'s parent: %s' % (item.id,str(e)))
|
||||
|
||||
def _schedule_reply(self,item,recipient,text):
|
||||
log_log('scheduling reply to %s:%s: %s' % (item.id if item else '""',recipient or '""',text))
|
||||
if item:
|
||||
ndata = redis_llen('reddit:replies')
|
||||
if ndata > 0:
|
||||
prev_item = redis_lindex('reddit:replies',ndata-1)
|
||||
if prev_item:
|
||||
prev_parts=prev_item.split(':',2)
|
||||
prev_fullname=prev_parts[0]
|
||||
prev_recipient=prev_parts[1]
|
||||
prev_text=prev_parts[2]
|
||||
if prev_fullname==item.fullname:
|
||||
log_log('Appending to previous item, also for the same fullname')
|
||||
new_text=prev_text+"\n\n"+text
|
||||
redis_lset('reddit:replies',ndata-1,(item.fullname if item else "")+":"+(recipient or "")+":"+new_text)
|
||||
return
|
||||
|
||||
redis_rpush('reddit:replies',(item.fullname if item else "")+":"+(recipient or "")+":"+text)
|
||||
|
||||
def _post_next_reply(self):
|
||||
data=redis_lindex('reddit:replies',0)
|
||||
if not data:
|
||||
return False
|
||||
parts=data.split(':',2)
|
||||
fullname=parts[0]
|
||||
recipient=parts[1]
|
||||
text=parts[2]
|
||||
|
||||
text = text.replace('\n\n','\n\n \n\n').replace('\n',' \n')
|
||||
|
||||
try:
|
||||
if recipient:
|
||||
# PM
|
||||
self.reddit.send_message(recipient,"Reply from %s"%self.login,text,raise_captcha_exception=True)
|
||||
log_info('Posted message to %s: %s' % (recipient,text))
|
||||
else:
|
||||
# subreddit or reply to PM
|
||||
item = None
|
||||
log_info('looking for "%s" in %s' % (str(fullname),str(self.items_cache)))
|
||||
if fullname in self.items_cache:
|
||||
item=self.items_cache[fullname]
|
||||
if not item:
|
||||
item=self.reddit.get_info(thing_id=fullname)
|
||||
if not item:
|
||||
log_error('Failed to find item %s to post %s' % (fullname,text))
|
||||
redis_lpop('reddit:replies')
|
||||
return True
|
||||
reply_item=item.reply(text)
|
||||
log_info('Posted reply to %s: %s' % (fullname,text))
|
||||
if reply_item and hasattr(reply_item,'id'):
|
||||
redis_sadd('reddit:last_seen_ids',reply_item.id)
|
||||
|
||||
redis_lpop('reddit:replies')
|
||||
|
||||
except praw.errors.RateLimitExceeded,e:
|
||||
log_info('Rate limited trying to send %s, will retry: %s' % (data,str(e)))
|
||||
return False
|
||||
except Exception,e:
|
||||
log_error('Error sending %s, will retry: %s' % (data,str(e)))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def canonicalize(self,name):
|
||||
return name.lower()
|
||||
|
||||
def update(self):
|
||||
return True
|
||||
|
||||
def _check(self):
|
||||
now=time.time()
|
||||
if now-self.last_update_time < self.update_period:
|
||||
return True
|
||||
self.last_update_time=now
|
||||
|
||||
if not self.last_seen_ids:
|
||||
self.last_seen_ids=redis_smembers('reddit:last_seen_ids')
|
||||
log_log('loaded last seen ids: %s ' % str(self.last_seen_ids))
|
||||
|
||||
if self.logged_in:
|
||||
# get_unread doesn't seem to work
|
||||
messages=self.reddit.get_inbox()
|
||||
for message in messages:
|
||||
if not message.was_comment:
|
||||
self._parse(message,True)
|
||||
|
||||
sr=self.reddit.get_subreddit("+".join(self.subreddits))
|
||||
comments=sr.get_comments(limit=self.load_limit)
|
||||
for comment in comments:
|
||||
self._parse(comment,False)
|
||||
|
||||
while self._post_next_reply():
|
||||
pass
|
||||
|
||||
log_log('RedditNetwork: update done in %.1f seconds' % float(time.time()-self.last_update_time))
|
||||
return True
|
||||
|
||||
def run(self):
|
||||
while not self.stop:
|
||||
try:
|
||||
self._check()
|
||||
except Exception,e:
|
||||
log_error('Exception in RedditNetwork:_check: %s' % str(e))
|
||||
time.sleep(1)
|
||||
|
||||
RegisterNetwork("reddit",RedditNetwork)
|
Loading…
Reference in a new issue