Skip to content
This repository has been archived by the owner on Apr 12, 2021. It is now read-only.

Commit

Permalink
Use the new ChatHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
nickoala committed Oct 29, 2015
1 parent 4bc71f4 commit f77a1b5
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 248 deletions.
109 changes: 46 additions & 63 deletions examples/chatbox_nodb.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
import sys
import re
import telepot
from telepot.delegate import per_chat_id_in, call, create_open

"""
$ python3.2 chatbox_nodb.py <token> <owner_id>
Expand Down Expand Up @@ -50,21 +49,18 @@ def unread_per_chat(self):
return [(k,len(v)) for k,v in self._db.items()]


# See `ChatBox` constructor to see how this class is used.
# Being a subclass of `ChatHandler` is useful, for it gives you many facilities,
# e.g. listener, sender, etc
# Accept commands from owner. Give him unread messages.
class OwnerHandler(telepot.helper.ChatHandler):
def __init__(self, seed_tuple, store):
super(OwnerHandler, self).__init__(*seed_tuple)
self.listener.timeout = 20 # timeout after 20 seconds of inactivity
def __init__(self, seed_tuple, timeout, store):
super(OwnerHandler, self).__init__(seed_tuple, timeout)
self._store = store

def _read_messages(self, messages):
for msg in messages:
# assume all messages are text
self.sender.sendMessage(msg['text'])

def _handle(self, msg):
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance2(msg)

if content_type != 'text':
Expand Down Expand Up @@ -104,36 +100,39 @@ def _handle(self, msg):
else:
self.sender.sendMessage("I don't understand")

def run(self):
self._handle(self.initial_message)

while 1:
# Use listener to wait for next message from owner
msg = self.listener.wait(chat__id=self.chat_id)
self._handle(msg)
class MessageSaver(telepot.helper.Monitor):
def __init__(self, seed_tuple, store, exclude):
# The `capture` criteria means to capture all messages.
super(MessageSaver, self).__init__(seed_tuple, capture=[{'_': lambda msg: True}])
self._store = store
self._exclude = exclude

# Store every message, except those whose sender is in the exclude list, or non-text messages.
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance2(msg)

# See `ChatBox` constructor to see how this class is used.
class NewcomerHandler(telepot.helper.ChatHandler):
def __init__(self, seed_tuple):
super(NewcomerHandler, self).__init__(*seed_tuple)
if chat_id in self._exclude:
print('Chat id %d is excluded.' % chat_id)
return

# The action is very simple: just send a welcome message.
def run(self):
print('NewcomerHandler: sending welcome')
self.sender.sendMessage('Hello!')
if content_type != 'text':
print('Content type %s is ignored.' % content_type)
return

print('Storing message: %s' % msg)
self._store.put(msg)


import threading

# See `ChatBox` constructor to see how this class, combined with `custom_thread`
# function below, is used to wrap around a `NewcomerHandler` object to provide
# a custom delegate implementation.
class CustomThread(threading.Thread):
def start(self):
print('CustomThread starting ...')
super(CustomThread, self).start()

# Note how this function wraps around the `call()` function below to implement
# a custom thread for delegation.
def custom_thread(func):
def f(seed_tuple):
target = func(seed_tuple)
Expand All @@ -148,56 +147,40 @@ def f(seed_tuple):
return f


from telepot.delegate import per_chat_id_in, call, create_run

class ChatBox(telepot.DelegatorBot):
def __init__(self, token, owner_id):
self._owner_id = owner_id
self._seen = set()
self._store = UnreadStore()

super(ChatBox, self).__init__(token, [
# For each owner, create an OwnerHandler and spawn a thread
# around its `run()` method.
(per_chat_id_in([owner_id]), create_run(OwnerHandler, self._store)),
# Note how extra arguments are supplied to OwnerHandler's constructor.

# For non-owners, just store the message. Since this is very simple,
# I choose to do it in a method - no objects required.
(self._others, call(self._store_message, self._store)),
# Note how extra arguments are supplied to the method.

# For non-owners who have never been seen,
# create a NewcomerHandler and spawn a *custom* thread
# around its `run()` method.
(self._newcomer, custom_thread(create_run(NewcomerHandler))),

# Note that the 2nd and 3rd condition are not exclusive. For a newcomer,
# both are triggered - that is, a welcome message is sent AND the message
# is stored.
])

def _others(self, msg):
chat_id = msg['chat']['id']
return [] if chat_id != self._owner_id else None
# [] non-hashable --> delegates are independent, no need to associate with a seed.
# Here is a delegate to specially handle owner commands.
(per_chat_id_in([owner_id]), create_open(OwnerHandler, 20, self._store)),

# Seed is always the same, meaning only one MessageSaver is ever spawned for entire application.
(lambda msg: 1, create_open(MessageSaver, self._store, exclude=[owner_id])),

def _newcomer(self, msg):
# For senders never seen before, send him a welcome message.
(self._is_newcomer, custom_thread(call(self._send_welcome))),
])

# seed-calculating function: use returned value to indicate whether to spawn a delegate
def _is_newcomer(self, msg):
chat_id = msg['chat']['id']
if chat_id == self._owner_id:
return None
if chat_id == self._owner_id: # Sender is owner
return None # No delegate spawned

if chat_id in self._seen:
return None
if chat_id in self._seen: # Sender has been seen before
return None # No delegate spawned

self._seen.add(chat_id)
return []
# [] non-hashable --> delegates are independent, no need to associate with a seed.
return [] # non-hashable ==> delegates are independent, no seed association is made.

def _store_message(self, seed_tuple, store):
msg = seed_tuple[1]
print('Storing message: %s' % msg)
store.put(msg)
def _send_welcome(self, seed_tuple):
chat_id = seed_tuple[1]['chat']['id']

print('Sending welcome ...')
self.sendMessage(chat_id, 'Hello!')


TOKEN = sys.argv[1]
Expand Down
117 changes: 45 additions & 72 deletions examples/chatboxa_nodb.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import os
import sys
import re
import traceback
import asyncio
import telepot
import telepot.async
from telepot.delegate import per_chat_id_in
from telepot.async.delegate import call, create_open

""" Python3.4.3 or newer
Expand Down Expand Up @@ -54,15 +52,10 @@ def unread_per_chat(self):
return [(k,len(v)) for k,v in self._db.items()]


# See `ChatBox` constructor to see how this class is used.
# Being a subclass of `ChatHandler` is useful, for it gives you many facilities,
# e.g. listener, sender, etc
# Accept commands from owner. Give him unread messages.
class OwnerHandler(telepot.helper.ChatHandler):
WAIT_TIMEOUT = 20

def __init__(self, seed_tuple, store):
super(OwnerHandler, self).__init__(*seed_tuple)
self.listener.timeout = 20 # timeout after 20 seconds of inactivity
def __init__(self, seed_tuple, timeout, store):
super(OwnerHandler, self).__init__(seed_tuple, timeout)
self._store = store

@asyncio.coroutine
Expand All @@ -72,9 +65,9 @@ def _read_messages(self, messages):
yield from self.sender.sendMessage(msg['text'])

@asyncio.coroutine
def _handle(self, msg):
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance2(msg)

if content_type != 'text':
yield from self.sender.sendMessage("I don't understand")
return
Expand Down Expand Up @@ -112,34 +105,29 @@ def _handle(self, msg):
else:
yield from self.sender.sendMessage("I don't understand")

@asyncio.coroutine
def run(self):
yield from self._handle(self.initial_message)

try:
while 1:
# Use listener to wait for next message from owner
msg = yield from asyncio.wait_for(self.listener.wait(chat__id=self.chat_id), self.WAIT_TIMEOUT)
yield from self._handle(msg)
except:
traceback.print_exc()
# display exceptions immediately
class MessageSaver(telepot.helper.Monitor):
def __init__(self, seed_tuple, store, exclude):
# The `capture` criteria means to capture all messages.
super(MessageSaver, self).__init__(seed_tuple, capture=[{'_': lambda msg: True}])
self._store = store
self._exclude = exclude

# Store every message, except those whose sender is in the exclude list, or non-text messages.
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance2(msg)

# See `ChatBox` constructor to see how this class is used.
class NewcomerHandler(telepot.helper.ChatHandler):
def __init__(self, seed_tuple):
super(NewcomerHandler, self).__init__(*seed_tuple)
if chat_id in self._exclude:
print('Chat id %d is excluded.' % chat_id)
return

# The action is very simple: just send a welcome message.
@asyncio.coroutine
def run(self):
print('NewcomerHandler: sending welcome')
yield from self.sender.sendMessage('Hello!')
if content_type != 'text':
print('Content type %s is ignored.' % content_type)
return

print('Storing message: %s' % msg)
self._store.put(msg)

from telepot.delegate import per_chat_id_in
from telepot.async.delegate import call, create_run

class ChatBox(telepot.async.DelegatorBot):
def __init__(self, token, owner_id):
Expand All @@ -148,49 +136,34 @@ def __init__(self, token, owner_id):
self._store = UnreadStore()

super(ChatBox, self).__init__(token, [
# For each owner, create an OwnerHandler and obtain a coroutine object
# from its `run()` method.
(per_chat_id_in([owner_id]), create_run(OwnerHandler, self._store)),
# Note how extra arguments are supplied to OwnerHandler's constructor.

# For non-owners, just store the message. Since this is very simple,
# I choose to do it in a method - no objects required.
(self._others, call(self._store_message, self._store)),
# Note how extra arguments are supplied to the method.

# For non-owners who have never been seen,
# create a NewcomerHandler and obtain a coroutine object
# from its `run()` method.
(self._newcomer, create_run(NewcomerHandler)),

# Note that the 2nd and 3rd condition are not exclusive. For a newcomer,
# both are triggered - that is, a welcome message is sent AND the message
# is stored.
])

def _others(self, msg):
chat_id = msg['chat']['id']
return [] if chat_id != self._owner_id else None
# [] non-hashable --> delegates are independent, no need to associate with a seed.
# Here is a delegate to specially handle owner commands.
(per_chat_id_in([owner_id]), create_open(OwnerHandler, 20, self._store)),

def _newcomer(self, msg):
# Seed is always the same, meaning only one MessageSaver is ever spawned for entire application.
(lambda msg: 1, create_open(MessageSaver, self._store, exclude=[owner_id])),

# For senders never seen before, send him a welcome message.
(self._is_newcomer, call(self._send_welcome)),
])

# seed-calculating function: use returned value to indicate whether to spawn a delegate
def _is_newcomer(self, msg):
chat_id = msg['chat']['id']
if chat_id == self._owner_id:
return None
if chat_id == self._owner_id: # Sender is owner
return None # No delegate spawned

if chat_id in self._seen:
return None
if chat_id in self._seen: # Sender has been seen before
return None # No delegate spawned

self._seen.add(chat_id)
return []
# [] non-hashable --> delegates are independent, no need to associate with a seed.
return [] # non-hashable ==> delegates are independent, no seed association is made.

# Must be a coroutine because it is used as a delegate
@asyncio.coroutine
def _store_message(self, seed_tuple, store):
msg = seed_tuple[1]
print('Storing message: %s' % msg)
store.put(msg)
def _send_welcome(self, seed_tuple):
chat_id = seed_tuple[1]['chat']['id']

print('Sending welcome ...')
yield from self.sendMessage(chat_id, 'Hello!')


TOKEN = sys.argv[1]
Expand Down
29 changes: 9 additions & 20 deletions examples/counter.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,26 @@
import sys
import telepot
from telepot.delegate import create_run
from telepot.delegate import per_chat_id, create_open

"""
$ python2.7 count.py <token>
$ python2.7 counter.py <token>
Count number of messages. Start over if silent for 10 seconds.
"""

# Being a subclass of ChatHandler is very useful, for it gives you:
# listener, sender, etc - a lot of facilities.
class MessageCounter(telepot.helper.ChatHandler):
def __init__(self, seed_tuple):
super(MessageCounter, self).__init__(*seed_tuple)
self.listener.timeout = 10
# conversation ends if no more messages after 10 seconds
def __init__(self, seed_tuple, timeout):
super(MessageCounter, self).__init__(seed_tuple, timeout)
self._count = 0

def run(self):
count = 1
self.sender.sendMessage(count)
# `sender` lets you send messages without mentioning chat_id every time

while 1:
# wait for user's next message
msg = self.listener.wait(chat__id=self.chat_id)
count += 1
self.sender.sendMessage(count)
def on_message(self, msg):
self._count += 1
self.sender.sendMessage(self._count)


TOKEN = sys.argv[1] # get token from command-line

bot = telepot.DelegatorBot(TOKEN, [
# For each chat id, create a MessageCounter and delegate to its `run()` method
(lambda msg: msg['chat']['id'], create_run(MessageCounter)),
(per_chat_id(), create_open(MessageCounter, timeout=10)),
])
bot.notifyOnMessage(run_forever=True)
Loading

0 comments on commit f77a1b5

Please sign in to comment.