Skip to content

Commit

Permalink
Smart availability
Browse files Browse the repository at this point in the history
Now you don't need to use `/available` and `/unavailable` commands. They are actually disabled.
Aviability sets to 'available' automatically when you reply to user. It drops to 'unavailable' after amount of time, specified in config file (by default: 1h)

Now you don't need to reply to message using Telegram Reply feature. You now have to press 'Reply' inline button. It sets admins status to replying. This status expire after 15mins. Can be adjusted in config file.
  • Loading branch information
p-hash committed Aug 1, 2016
1 parent 4e7cd56 commit cd6e513
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 76 deletions.
12 changes: 10 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

# Bot's token. Obtain yours from https://telegram.me/botfather
token = "<TOKEN>"
# Your telegram user id. Get it from @my_id_bot
Expand All @@ -11,6 +9,16 @@
db_auth = 'mongodb://<user>:<password>@<my.mongodb.com>:<port>/<database>'
db_name = '<database_name>'

# Smart availability: your available status will expire after this amount of seconds
availability_expiration = 60 * 60 # 1h

# When you click `Reply` button, your replying state will expire after this amount of seconds
replying_expiration = 15 * 60 # 15min


# You can define all those variables in local_config.py
# It will let you `git pull` without merging
# Take care that updates are not guaranteed to be backward compatible
try:
from local_config import *
except ImportError:
Expand Down
28 changes: 18 additions & 10 deletions db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pymongo import MongoClient
from bson.objectid import ObjectId
from time import time

import model
import config

Expand Down Expand Up @@ -94,16 +95,16 @@ def __init__(self, coll):
self.data['_id'] = result.inserted_id

self._replying_to = None
self._replying_to_expiration = config.replying_expiration
self._availability_expiration = config.availability_expiration
self._last_seen = time() - self._availability_expiration

@property
def availability(self):
return self.data.get('availability') or ''

@availability.setter
def availability(self, value):
if value in ['available', 'unavailable']:
self.data['availability'] = value
self.coll.update_one({'_id': self.data['_id']}, {'$set': self.data})
if time() - self._last_seen > self._availability_expiration:
return 'unavailable'
else:
return 'available'

@property
def blockmsg(self):
Expand Down Expand Up @@ -134,11 +135,18 @@ def startmsg(self, value):

@property
def replying_to(self):
return self._replying_to # todo: set expiration
if time() - self._last_seen > self._replying_to_expiration: # if admin wasn't here for a quite long time
self._replying_to = None # ignore last replying_to
return self._replying_to

@replying_to.setter
def replying_to(self, value):
self._replying_to = value # todo: set expiration
self._replying_to = value
self._last_seen = time()

@property
def last_seen(self):
return self._last_seen


__db_client = MongoClient(config.db_auth)
Expand Down
36 changes: 21 additions & 15 deletions model.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,7 @@ def short_id(value):
# adds Dictionaryable behavior and marks models which can be stored in db
class Model(types.Dictionaryable):
def to_dic(self):
d = {}
for k, v in vars(self).items():
if isinstance(v, Model):
d[k] = v.to_dic()
elif not str(k).startswith('_'):
if str(k) == 'id':
k = '_id'
d[k] = v
return d
raise NotImplementedError()


# Represents User and adds extra fields to telepot's class
Expand All @@ -55,6 +47,17 @@ def __init__(self, *args, **kwargs):
self.blocked = kwargs.get('blocked')
super().__init__(*args)

def to_dic(self):
d = {}
for k, v in vars(self).items():
if isinstance(v, Model):
d[k] = v.to_dic()
elif not str(k).startswith('_'):
if str(k) == 'id':
k = '_id'
d[k] = v
return d

def update(self, data):
assert int(self.id) == int(data.id)
self.first_name = data.first_name
Expand Down Expand Up @@ -92,16 +95,22 @@ def __init__(self, *args, **kwargs):
if args:
self.id = bson.ObjectId()
super().__init__(*args)
if self.from_user.id != config.my_id:
self.with_user = self.from_user.id
else:
self.with_user = None
else:
super().__init__(
kwargs['message_id'],
User(**kwargs['from_user']),
kwargs['date'],
Chat(**kwargs['chat']),
kwargs['content_type'],
options={})
options={}
)
self.id = kwargs['_id']
self.text = kwargs['text']
self.with_user = kwargs['with']

def to_dic(self):
d = dict()
Expand All @@ -114,15 +123,12 @@ def to_dic(self):
d['chat'] = self.chat.to_dic()
d['content_type'] = self.content_type

if self.from_user.id == config.my_id and self.reply_to_message:
d['with'] = self.reply_to_message.forward_from.id
else:
d['with'] = self.from_user.id
d['with'] = self.with_user

if self.text:
d['text'] = self.text
else:
d['text'] = 'Non text message: /msg' + short_id(self.id)
d['text'] = 'Non text message: /msg' + d['short_id']
return d

def __format__(self, format_spec):
Expand Down
86 changes: 37 additions & 49 deletions proxy_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,44 +386,20 @@ def show_msg(message):
bot.forward_message(config.my_id, old_msg.chat.id, old_msg.message_id)


# command for admin to set his/her status as available
# this will simply re-write the availability.txt file with the text "available"
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, commands=["available"])
def command_available(message):
bot.send_message(
message.chat.id,
"Your Status has been set as *Available*",
parse_mode="Markdown"
)
db.common.availability = 'available'


# command for admin to set his/her status as unavailable
# this will simply re-write the availability.txt file with the text "unavailable"
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, commands=["unavailable"])
def command_unavailable(message):
bot.send_message(
message.chat.id,
"Your Status has been set as *Unavailable*",
parse_mode="Markdown"
)
db.common.availability = 'unavailable'


# command for the admin to check his/her current status.
# The dictionary.check_status() method simply reads the text in the availability.txt file
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, commands=["checkstatus"])
def command_checkstatus(message):
if db.common.availability == 'unavailable':
bot.send_message(
message.chat.id,
"Your current status is *Unavailable*",
"Your current status is *Unavailable*",
parse_mode="Markdown"
)
else:
bot.send_message(
message.chat.id,
"Your current status is *Available*",
"Your current status is *Available*",
parse_mode="Markdown"
)

Expand Down Expand Up @@ -460,9 +436,12 @@ def handle_all(message):
db.msg.create(message) # log message in db

text = 'New message from {user}:\n {msg}'.format(user=user, msg=message)
markup = None

# text, markup = get_usercard_markup(user, 0) # generate usercard
markup = telebot.types.InlineKeyboardMarkup()
markup.add(
telebot.types.InlineKeyboardButton('Show Log', callback_data='log_{}_0'.format(user.id)),
telebot.types.InlineKeyboardButton('Block', callback_data='user_block_{}'.format(user.id)),
telebot.types.InlineKeyboardButton('Reply', callback_data='reply_{}'.format(user.id))
)
bot.send_message(config.my_id, text, reply_markup=markup) # send it

if message.content_type != 'text':
Expand All @@ -489,6 +468,7 @@ def my_text(message):
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='typing')
bot.send_message(user_id, message.text)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(config.my_id, "No one to reply!")
Expand All @@ -497,9 +477,10 @@ def my_text(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["sticker"])
def my_sticker(message):
if db.common.replying_to:
chat_id = db.common.replying_to
bot.send_chat_action(chat_id, action='typing')
bot.send_sticker(chat_id, message.sticker.file_id)
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='typing')
bot.send_sticker(user_id, message.sticker.file_id)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(config.my_id, "No one to reply")
Expand All @@ -508,9 +489,10 @@ def my_sticker(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["photo"])
def my_photo(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_chat_action(who_to_send_id, action='upload_photo')
bot.send_photo(who_to_send_id, list(message.photo)[-1].file_id)
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='upload_photo')
bot.send_photo(user_id, list(message.photo)[-1].file_id)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand All @@ -519,8 +501,9 @@ def my_photo(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["voice"])
def my_voice(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_voice(who_to_send_id, message.voice.file_id, duration=message.voice.duration)
user_id = db.common.replying_to
bot.send_voice(user_id, message.voice.file_id, duration=message.voice.duration)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand All @@ -529,9 +512,10 @@ def my_voice(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["document"])
def my_document(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_chat_action(who_to_send_id, action='upload_document')
bot.send_document(who_to_send_id, data=message.document.file_id)
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='upload_document')
bot.send_document(user_id, data=message.document.file_id)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand All @@ -540,15 +524,16 @@ def my_document(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["audio"])
def my_audio(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_chat_action(who_to_send_id, action='upload_audio')
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='upload_audio')
bot.send_audio(
who_to_send_id,
user_id,
performer=message.audio.performer,
audio=message.audio.file_id,
title=message.audio.title,
duration=message.audio.duration
)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand All @@ -557,9 +542,10 @@ def my_audio(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["video"])
def my_video(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_chat_action(who_to_send_id, action='upload_video')
bot.send_video(who_to_send_id, data=message.video.file_id, duration=message.video.duration)
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='upload_video')
bot.send_video(user_id, data=message.video.file_id, duration=message.video.duration)
message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand All @@ -569,9 +555,11 @@ def my_video(message):
@bot.message_handler(func=lambda message: message.chat.id == config.my_id, content_types=["location"])
def my_location(message):
if db.common.replying_to:
who_to_send_id = db.common.replying_to
bot.send_chat_action(who_to_send_id, action='find_location')
bot.send_location(who_to_send_id, latitude=message.location.latitude, longitude=message.location.longitude)
user_id = db.common.replying_to
bot.send_chat_action(user_id, action='find_location')
bot.send_location(user_id, latitude=message.location.latitude, longitude=message.location.longitude)

message.with_user = user_id # mark message as belonging to conversation with specified user
db.msg.create(message) # log message in db
else:
bot.send_message(message.chat.id, "No one to reply!")
Expand Down

0 comments on commit cd6e513

Please sign in to comment.