SHARE
TWEET

__init__.py

a guest Sep 23rd, 2019 96 in 341 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. from __future__ import absolute_import
  2. from PIL import Image
  3. import threading, requests, re, time, datetime, StringIO, json, random, logging, traceback, io, collections, os, flask,base64,PIL, pkg_resources,subprocess,zipfile,glob #imageio
  4. import octoprint.plugin, octoprint.util, octoprint.filemanager
  5. from flask.ext.babel import gettext
  6. from flask.ext.login import current_user
  7. from .telegramCommands import TCMD # telegramCommands.
  8. from .telegramNotifications import TMSG # telegramNotifications
  9. from .telegramNotifications import telegramMsgDict # dict of known notification messages
  10. from .emojiDict import telegramEmojiDict # dict of known emojis
  11. from babel.dates import format_date, format_datetime, format_time                            
  12. ####################################################
  13. #        TelegramListener Thread Class
  14. # Connects to Telegram and will listen for messages.
  15. # On incomming message the listener will process it.
  16. ####################################################
  17.  
  18. class TelegramListener(threading.Thread):
  19.     def __init__(self, main):
  20.         threading.Thread.__init__(self)
  21.         self.update_offset = 0
  22.         self.first_contact = True
  23.         self.main = main
  24.         self.do_stop = False
  25.         self.username = "UNKNOWN"
  26.         self._logger = main._logger.getChild("listener")
  27.         self.gEmo = self.main.gEmo
  28.        
  29.  
  30.     def run(self):
  31.         self._logger.debug("Try first connect.")
  32.         self.tryFirstContact()
  33.         # repeat fetching and processing messages unitil thread stopped
  34.         self._logger.debug("Listener is running.")
  35.         try:
  36.             while not self.do_stop:
  37.                 try:
  38.                     self.loop()
  39.                 except ExitThisLoopException:
  40.                     # do nothing, just go to the next loop
  41.                     pass
  42.         except Exception as ex:
  43.             self._logger.error("An Exception crashed the Listener: " + str(ex) + " Traceback: " + traceback.format_exc() )
  44.            
  45.         self._logger.debug("Listener exits NOW.")
  46.  
  47.     # Try to get first contact. Repeat every 120sek if no success
  48.     # or stop if task stopped
  49.     def tryFirstContact(self):
  50.         gotContact = False
  51.         while not self.do_stop and not gotContact:
  52.             try:
  53.                 self.username = self.main.test_token()
  54.                 gotContact = True
  55.                 self.set_status(gettext("Connected as %(username)s.", username=self.username), ok=True)
  56.             except Exception as ex:
  57.                 self.set_status(gettext("Got an exception while initially trying to connect to telegram (Listener not running: %(ex)s.  Waiting 2 minutes before trying again.)", ex=ex))
  58.                 time.sleep(120)
  59.        
  60.     def loop(self):
  61.         chat_id = ""
  62.         json = self.getUpdates()
  63.         try:
  64.             # seems like we got a message, so lets process it.
  65.             for message in json['result']:
  66.                 self.processMessage(message)
  67.         except ExitThisLoopException as exit:
  68.             raise exit
  69.         #wooooops. can't handle the message
  70.         except Exception as ex:
  71.             self._logger.error("Exception caught! " + str(ex))
  72.         self.set_status(gettext("Connected as %(username)s.", username=self.username), ok=True)
  73.         # we had first contact after octoprint startup
  74.         # so lets send startup message
  75.         if self.first_contact:
  76.             self.first_contact = False
  77.             self.main.on_event("PrinterStart",{})
  78.    
  79.     def set_update_offset(self, new_value):
  80.         if new_value >= self.update_offset:
  81.             self._logger.debug("Updating update_offset from {} to {}".format(self.update_offset, 1 + new_value))
  82.             self.update_offset = 1 + new_value
  83.         else:
  84.             self._logger.debug("Not changing update_offset - otherwise would reduce it from {} to {}".format(self.update_offset, 1 + new_value))
  85.    
  86.     def processMessage(self, message):
  87.         self._logger.debug("MESSAGE: " + str(message))
  88.         # Get the update_id to only request newer Messages the next time
  89.         self.set_update_offset(message['update_id'])
  90.         # no message no cookies
  91.         if 'message' in message and message['message']['chat']:
  92.        
  93.             chat_id, from_id = self.parseUserData(message) 
  94.            
  95.             # if we come here without a continue (discard message)
  96.             # we have a message from a known and not new user
  97.             # so let's check what he send us
  98.             # if message is a text message, we probably got a command
  99.             # when the command is not known, the following handler will discard it
  100.             if "text" in message['message']:
  101.                 self.handleTextMessage(message, chat_id, from_id)
  102.             # we got no message with text (command) so lets check if we got a file
  103.             # the following handler will check file and saves it to disk
  104.             elif "document" in message['message']:
  105.                 self.handleDocumentMessage(message, chat_id, from_id)
  106.             # we got message with notification for a new chat title photo
  107.             # so lets download it
  108.             elif "new_chat_photo" in message['message']:
  109.                 self.handleNewChatPhotoMessage(message, chat_id, from_id)
  110.             # we got message with notification for a deleted chat title photo
  111.             # so we do the same
  112.             elif "delete_chat_photo" in message['message']:
  113.                 self.handleDeleteChatPhotoMessage(message, chat_id, from_id)
  114.             # a member was removed from a group, so lets check if it's our bot and
  115.             # delete the group from our chats if it is our bot
  116.             elif "left_chat_member" in message['message']:
  117.                 self.handleLeftChatMemberMessage(message, chat_id, from_id)
  118.             # we are at the end. At this point we don't know what message type it is, so we do nothing
  119.             else:
  120.                 self._logger.warn("Got an unknown message. Doing nothing. Data: " + str(message))
  121.         elif 'callback_query' in message:
  122.             self.handleCallbackQuery(message)
  123.         else:
  124.             self._logger.warn("Response is missing .message or .message.chat or callback_query. Skipping it.")
  125.             raise ExitThisLoopException()
  126.  
  127.    
  128.     def handleCallbackQuery(self, message):
  129.         message['callback_query']['message']['text'] = message['callback_query']['data']
  130.         chat_id, from_id = self.parseUserData(message['callback_query'])
  131.         self.handleTextMessage(message['callback_query'],chat_id, from_id)
  132.  
  133.     def handleLeftChatMemberMessage(self, message, chat_id, from_id):
  134.         self._logger.debug("Message Del_Chat")
  135.         if message['message']['left_chat_member']['username'] == self.username[1:] and str(message['message']['chat']['id']) in self.main.chats:
  136.             del self.main.chats[str(message['message']['chat']['id'])]
  137.             # do a self._settings.save() ???
  138.             self._logger.debug("Chat deleted")
  139.            
  140.     def handleDeleteChatPhotoMessage(self, message, chat_id, from_id):
  141.         self._logger.debug("Message Del_Chat_Photo")
  142.         try:
  143.             os.remove(self.main.get_plugin_data_folder()+"/img/user/pic" +str(message['message']['chat']['id'])+".jpg")
  144.             self._logger.debug("File removed")
  145.         except OSError:
  146.             pass
  147.            
  148.     def handleNewChatPhotoMessage(self, message, chat_id, from_id):
  149.         self._logger.debug("Message New_Chat_Photo")
  150.         # only if we know the chat
  151.         if str(message['message']['chat']['id']) in self.main.chats:
  152.             self._logger.debug("New_Chat_Photo Found User")
  153.             kwargs = {'chat_id':int(message['message']['chat']['id']), 'file_id': message['message']['new_chat_photo'][0]['file_id'] }
  154.             t = threading.Thread(target=self.main.get_usrPic, kwargs=kwargs)
  155.             t.daemon = True
  156.             t.run()
  157.            
  158.     def handleDocumentMessage(self, message, chat_id, from_id):
  159.         # first we have to check if chat or group is allowed to upload
  160.         from_id = chat_id
  161.         if not self.main.chats[chat_id]['private']: #is this needed? can one send files from groups to bots?
  162.             from_id = str(message['message']['from']['id'])
  163.         # is /upload allowed?
  164.         if self.main.isCommandAllowed(chat_id,from_id,'/upload'):
  165.             self.main.track_action("command/upload_exec")
  166.             try:
  167.                 file_name = message['message']['document']['file_name']
  168.                 #if not (file_name.lower().endswith('.gcode') or file_name.lower().endswith('.gco') or file_name.lower().endswith('.g')):
  169.                 self._logger.info(str(file_name.lower().split('.')[-1]))
  170.                 isZipFile = False
  171.                 if not octoprint.filemanager.valid_file_type(file_name,"machinecode"):
  172.                     #giloser 09/05/2019 try to zip the gcode file to lower the size
  173.                     if file_name.lower().endswith('.zip'):
  174.                         isZipFile = True
  175.                     else:
  176.                         self.main.send_msg(self.gEmo('warning') + " Sorry, I only accept files with .gcode, .gco or .g or .zip extension.", chatID=chat_id)
  177.                         raise ExitThisLoopException()
  178.                 # download the file
  179.                 if self.main.version >= 1.3:
  180.                     target_filename = "TelegramPlugin/"+file_name
  181.                     from octoprint.server.api.files import _verifyFolderExists
  182.                     if not _verifyFolderExists(octoprint.filemanager.FileDestinations.LOCAL, "TelegramPlugin"):
  183.                         self.main._file_manager.add_folder(octoprint.filemanager.FileDestinations.LOCAL,"TelegramPlugin")
  184.                 else:
  185.                     target_filename = "telegram_"+file_name
  186.                 # for parameter no_markup see _send_edit_msg()
  187.                 self.main.send_msg(self.gEmo('save') + gettext(" Saving file {}...".format(target_filename)), chatID=chat_id)
  188.                 requests.get(self.main.bot_url + "/sendChatAction", params = {'chat_id': chat_id, 'action': 'upload_document'}, proxies=self.getProxies())
  189.                 data = self.main.get_file(message['message']['document']['file_id'])
  190.                 #giloser 09/05/2019 try to zip the gcode file to lower the size
  191.                 if isZipFile:
  192.                     try:
  193.                         #stream = octoprint.filemanager.util.StreamWrapper(target_filename, io.BytesIO(data))
  194.                         #self.main._file_manager.add_folder(self.get_plugin_data_folder() , "tmpzip", ignore_existing=True)
  195.                         zip_filename= self.main.get_plugin_data_folder()+"/tmpzip/" +file_name
  196.                         with open(zip_filename,'w') as f:
  197.                             f.write(data)
  198.                         #self.main._file_manager.add_file(octoprint.filemanager.FileDestinations.LOCAL, target_filename, stream, allow_overwrite=True)
  199.                     except Exception as ex:
  200.                         self._logger.info("Exception occured during save file : "+ traceback.format_exc() )
  201.                
  202.                     self._logger.info('read archive '  + zip_filename)
  203.                     try:
  204.                         zf = zipfile.ZipFile(zip_filename, 'r')
  205.                         self._logger.info('namelist ')
  206.                         list_files = zf.namelist()
  207.                         stringmsg = ""
  208.                         for filename in list_files:
  209.                             if octoprint.filemanager.valid_file_type(filename,"machinecode"):
  210.                                 try:
  211.                                     data = zf.read(filename)
  212.                                     stream = octoprint.filemanager.util.StreamWrapper(filename, io.BytesIO(data))
  213.                                     if self.main.version >= 1.3:
  214.                                         target_filename = "TelegramPlugin/"+filename
  215.                                         from octoprint.server.api.files import _verifyFolderExists
  216.                                         if not _verifyFolderExists(octoprint.filemanager.FileDestinations.LOCAL, "TelegramPlugin"):
  217.                                             self.main._file_manager.add_folder(octoprint.filemanager.FileDestinations.LOCAL,"TelegramPlugin")
  218.                                     else:
  219.                                         target_filename = "telegram_"+filename
  220.                                     self.main._file_manager.add_file(octoprint.filemanager.FileDestinations.LOCAL, target_filename, stream, allow_overwrite=True)
  221.                                     if stringmsg == "":
  222.                                         stringmsg = self.gEmo('upload') + " I've successfully saved the file you sent me as {}".format(target_filename)
  223.                                     else:
  224.                                         stringmsg = stringmsg + ", " + target_filename
  225.                                     # for parameter msg_id see _send_edit_msg()
  226.                                 except Exception as ex:
  227.                                     self._logger.info("Exception occured during processing of a file: "+ traceback.format_exc() )
  228.                             else:
  229.                                 self._logger.info('File '+ filename + ' is not a valide filename ')
  230.                     except Exception as ex:
  231.                         self.main.send_msg(self.gEmo('warning') + " Sorry, Problem managing the zip file.", chatID=chat_id)
  232.                         self._logger.info("Exception occured during processing of a file: "+ traceback.format_exc() )
  233.                         raise ExitThisLoopException()
  234.                     finally:
  235.                         self._logger.info('will now close the zip file')
  236.                         zf.close()
  237.                     if stringmsg != "":
  238.                         self.main.send_msg(stringmsg,msg_id=self.main.getUpdateMsgId(chat_id),chatID=chat_id)
  239.                     else:
  240.                         self.main.send_msg(self.gEmo('warning') + " Something went wrong during processing of your file."+self.gEmo('mistake')+" Sorry. More details are in octoprint.log.",msg_id=self.main.getUpdateMsgId(chat_id),chatID=chat_id)
  241.                         self._logger.info("Exception occured during processing of a file: "+ traceback.format_exc() )
  242.                    
  243.                     #self.main._file_manager.remove_file(zip_filename)
  244.                     os.remove(zip_filename)
  245.                 else:
  246.                     stream = octoprint.filemanager.util.StreamWrapper(file_name, io.BytesIO(data))
  247.                     self.main._file_manager.add_file(octoprint.filemanager.FileDestinations.LOCAL, target_filename, stream, allow_overwrite=True)
  248.                     # for parameter msg_id see _send_edit_msg()
  249.                     self.main.send_msg(self.gEmo('upload') + " I've successfully saved the file you sent me as {}.".format(target_filename),msg_id=self.main.getUpdateMsgId(chat_id),chatID=chat_id)
  250.             except ExitThisLoopException:
  251.                 pass
  252.             except Exception as ex:
  253.                 self.main.send_msg(self.gEmo('warning') + " Something went wrong during processing of your file."+self.gEmo('mistake')+" Sorry. More details are in octoprint.log.",msg_id=self.main.getUpdateMsgId(chat_id),chatID=chat_id)
  254.                 self._logger.debug("Exception occured during processing of a file: "+ traceback.format_exc() )
  255.         else:
  256.             self._logger.warn("Previous file was from an unauthorized user.")
  257.             self.main.send_msg("Don't feed the octopuses! " + self.gEmo('octo'),chatID=chat_id)
  258.            
  259.     def handleTextMessage(self, message, chat_id, from_id):
  260.         # We got a chat message.
  261.         # handle special messages from groups (/commad@BotName)
  262.         command = str(message['message']['text'].split('@')[0].encode('utf-8'))
  263.         parameter = ""
  264.         # TODO: Do we need this anymore?
  265.         # reply_to_messages will be send on value inputs (eg notification height)
  266.         # but also on android when pushing a button. Then we have to switch command and parameter.
  267.         #if "reply_to_message" in message['message'] and "text" in message['message']['reply_to_message']:
  268.             #command = message['message']['reply_to_message']['text']
  269.             #parameter = message['message']['text']
  270.             #if command.encode('utf-8') not in [str(k.encode('utf-8')) for k in self.main.tcmd.commandDict.keys()]:
  271.                 #command = message['message']['text']
  272.                 #parameter = message['message']['reply_to_message']['text']
  273.         # if command is with parameter, get the parameter
  274.         if any((k+"_") in command for k,v in self.main.tcmd.commandDict.iteritems() if 'param' in v):
  275.             parameter = '_'.join(command.split('_')[1:])
  276.             command = command.split('_')[0]
  277.         self._logger.info("Got a command: '" + str(command.encode('utf-8')) + "' with parameter: '" + str(parameter.encode('utf-8')) + "' in chat " + str(message['message']['chat']['id']))
  278.         # is command  known?
  279.         if command not in self.main.tcmd.commandDict:
  280.             # we dont know the command so skip the message
  281.             self._logger.warn("Previous command was an unknown command.")
  282.             self.main.send_msg("I do not understand you! " + self.gEmo('mistake'),chatID=chat_id)
  283.             raise ExitThisLoopException()
  284.         # check if user is allowed to execute the command
  285.         if self.main.isCommandAllowed(chat_id,from_id,command):
  286.             # Track command
  287.             if command.startswith("/"):
  288.                 self.main.track_action("command/" + command[1:])
  289.             # execute command
  290.             self.main.tcmd.commandDict[command]['cmd'](chat_id,from_id,command,parameter)
  291.         else:
  292.             # user was not alloed to execute this command
  293.             self._logger.warn("Previous command was from an unauthorized user.")
  294.             self.main.send_msg("You are not allowed to do this! " + self.gEmo('notallowed'),chatID=chat_id)
  295.    
  296.     def parseUserData(self, message):
  297.         self.main.chats = self.main._settings.get(["chats"])
  298.         chat = message['message']['chat']
  299.         chat_id = str(chat['id'])
  300.         data = self.main.newChat # data for new user
  301.         # if we know the user or chat, overwrite data with user data
  302.         if chat_id in self.main.chats:
  303.             data = self.main.chats[chat_id]
  304.         # update data or get data for new user
  305.         data['type'] = chat['type'].upper()
  306.         if chat['type']=='group' or chat['type'] == 'supergroup':
  307.             data['private'] = False
  308.             data['title'] = chat['title']
  309.         elif chat['type']=='private':
  310.             data['private'] = True
  311.             data['title'] = ""
  312.             if "first_name" in chat:
  313.                 data['title'] += chat['first_name'] + " - "
  314.             if "last_name" in chat:
  315.                 data['title'] += chat['last_name'] + " - "
  316.             if "username" in chat:
  317.                 data['title'] += "@" + chat['username']
  318.         from_id = chat_id
  319.         # if message is from a group, chat_id will be left as id of group
  320.         # and from_id is set to id of user who send the message
  321.         if not data['private']:
  322.             if 'from' in message:
  323.                 from_id = str(message['from']['id'])
  324.             else:
  325.                 from_id = str(message['message']['from']['id'])
  326.             # if group accepts only commands from known users (allow_users = true, accept_commands=false)
  327.             # and user is not in known chats, then he is unknown and we dont wnat to listen to him.
  328.             if chat_id in self.main.chats:
  329.                 if self.main.chats[chat_id]['allow_users'] and from_id not in self.main.chats and not self.main.chats[chat_id]['accept_commands']:
  330.                     self._logger.warn("Previous command was from an unknown user.")
  331.                     self.main.send_msg("I don't know you! Certainly you are a nice Person " + self.gEmo('heart'),chatID=chat_id)
  332.                     raise ExitThisLoopException()
  333.         # if we dont know the user or group, create new user
  334.         # send welcome message and skip message
  335.         if chat_id not in self.main.chats:
  336.             self.main.chats[chat_id] = data
  337.             self.main.send_msg(self.gEmo('info') + "Now I know you. Before you can do anything, go to OctoPrint Settings and edit some rights.",chatID=chat_id)
  338.             kwargs = {'chat_id':int(chat_id)}
  339.             t = threading.Thread(target=self.main.get_usrPic, kwargs=kwargs)
  340.             t.daemon = True
  341.             t.run()
  342.             self._logger.debug("Got new User")
  343.             raise ExitThisLoopException()
  344.         return (chat_id, from_id)
  345.    
  346.     def getUpdates(self):
  347.         self._logger.debug("listener: sending request with offset " + str(self.update_offset) + "...")
  348.         req = None
  349.        
  350.         # try to check for incoming messages. wait 120sek and repeat on failure
  351.         try:
  352.             if self.update_offset == 0 and self.first_contact:
  353.                 res = ["0","0"]
  354.                 while len(res) > 0:
  355.                     req = requests.get(self.main.bot_url + "/getUpdates", params={'offset':self.update_offset, 'timeout':0}, allow_redirects=False, timeout=10, proxies=self.getProxies())
  356.                     json = req.json()
  357.                     if not json['ok']:
  358.                         self.set_status(gettext("Response didn't include 'ok:true'. Waiting 2 minutes before trying again. Response was: %(response)s", json))
  359.                         time.sleep(120)
  360.                         raise ExitThisLoopException()
  361.                     if len(json['result']) > 0 and 'update_id' in json['result'][0]:
  362.                         self.set_update_offset(json['result'][0]['update_id'])
  363.                     res = json['result']
  364.                     if len(res) < 1:
  365.                         self._logger.debug("Ignoring message because first_contact is True.")
  366.                 if self.update_offset == 0:
  367.                     self.set_update_offset(0)
  368.             else:
  369.                 req = requests.get(self.main.bot_url + "/getUpdates", params={'offset':self.update_offset, 'timeout':30}, allow_redirects=False, timeout=40, proxies=self.getProxies())
  370.         except requests.exceptions.Timeout:
  371.             # Just start the next loop.
  372.             raise ExitThisLoopException()
  373.         except Exception as ex:
  374.             self.set_status(gettext("Got an exception while trying to connect to telegram API: %(exception)s. Waiting 2 minutes before trying again.", exception=ex))
  375.             time.sleep(120)
  376.             raise ExitThisLoopException()
  377.         if req.status_code != 200:
  378.             self.set_status(gettext("Telegram API responded with code %(status_code)s. Waiting 2 minutes before trying again.", status_code=req.status_code))
  379.             time.sleep(120)
  380.             raise ExitThisLoopException()
  381.         if req.headers['content-type'] != 'application/json':
  382.             self.set_status(gettext("Unexpected Content-Type. Expected: application/json. Was: %(type)s. Waiting 2 minutes before trying again.", type=req.headers['content-type']))
  383.             time.sleep(120)
  384.             raise ExitThisLoopException()
  385.         json = req.json()
  386.         if not json['ok']:
  387.             self.set_status(gettext("Response didn't include 'ok:true'. Waiting 2 minutes before trying again. Response was: %(response)s", json))
  388.             time.sleep(120)
  389.             raise ExitThisLoopException()
  390.         if "result" in json and len(json['result']) > 0:
  391.             for entry in json['result']:
  392.                 self.set_update_offset(entry['update_id'])
  393.         return json
  394.  
  395.     # stop the listener
  396.     def stop(self):
  397.         self.do_stop = True
  398.    
  399.     def set_status(self, status, ok=False):
  400.         if status != self.main.connection_state_str:
  401.             if self.do_stop:
  402.                 self._logger.debug("Would set status but do_stop is True: %s", status)
  403.                 return
  404.             if ok:
  405.                 self._logger.debug("Setting status: %s", status)
  406.             else:
  407.                 self._logger.error("Setting status: %s", status)
  408.         self.connection_ok = ok
  409.         self.main.connection_state_str = status
  410.  
  411.     def getProxies(self):
  412.         http_proxy = self.main._settings.get(["http_proxy"])
  413.         https_proxy = self.main._settings.get(["https_proxy"])
  414.         return {
  415.             'http': http_proxy,
  416.             'https': https_proxy
  417.             }
  418.  
  419. class TelegramPluginLoggingFilter(logging.Filter):
  420.     def filter(self, record):
  421.         for match in re.findall("[0-9]+:[a-zA-Z0-9_\-]+", record.msg):
  422.             new = re.sub("[0-9]", "1", re.sub("[a-z]", "a", re.sub("[A-Z]", "A", match)))
  423.             record.msg = record.msg.replace(match, new)
  424.         return True
  425.  
  426. class ExitThisLoopException(Exception):
  427.     pass
  428.  
  429. ########################################
  430. ########################################
  431. ############## THE PLUGIN ##############
  432. ########################################
  433. ########################################
  434. class TelegramPlugin(octoprint.plugin.EventHandlerPlugin,
  435.                      octoprint.plugin.SettingsPlugin,
  436.                      octoprint.plugin.StartupPlugin,
  437.                      octoprint.plugin.ShutdownPlugin,
  438.                      octoprint.plugin.TemplatePlugin,
  439.                      octoprint.plugin.SimpleApiPlugin,
  440.                      octoprint.plugin.AssetPlugin
  441.                      ):
  442.  
  443.     def __init__(self,version):
  444.         self.version = float(version)
  445.         # for more init stuff see on_after_startup()
  446.         self.thread = None
  447.         self.bot_url = None
  448.         self.chats = {}
  449.         self.connection_state_str = gettext("Disconnected.")
  450.         self.connection_ok = False
  451.         requests.packages.urllib3.disable_warnings()
  452.         self.updateMessageID = {}
  453.         self.shut_up = {}
  454.         self.send_messages = True
  455.         self.tcmd = None
  456.         self.tmsg = None
  457.         self.sending_okay_minute = None
  458.         self.sending_okay_count = 0
  459.         # initial settings for new chat. See on_after_startup()
  460.         # !!! sync with newUsrDict in on_settings_migrate() !!!
  461.         self.newChat = {}
  462.         # use of emojis see below at method gEmo()
  463.         self.emojis = {
  464.             'octo':     u'\U0001F419', #octopus
  465.             'mistake':  u'\U0001F616',
  466.             'notify': u'\U0001F514',
  467.             'shutdown' : u'\U0001F4A4',
  468.             'shutup':    u'\U0001F64A',
  469.             'noNotify': u'\U0001F515',
  470.             'notallowed': u'\U0001F62C',
  471.             'rocket': u'\U0001F680',
  472.             'save': u'\U0001F4BE',
  473.             'heart': u'\U00002764',
  474.             'info': u'\U00002139',
  475.             'settings': u'\U0001F4DD',
  476.             'clock': u'\U000023F0',
  477.             'height': u'\U00002B06',
  478.             'question': u'\U00002753',
  479.             'warning': u'\U000026A0',
  480.             'enter': u'\U0000270F',
  481.             'upload': u'\U0001F4E5',
  482.             'check': u'\U00002705',
  483.             'lamp': u'\U0001F4A1',
  484.             'movie': u'\U0001F3AC',
  485.             'finish': u'\U0001F3C1',
  486.             'cam': u'\U0001F3A6',
  487.             'hooray': u'\U0001F389',
  488.             'error': u'\U000026D4',
  489.             'play': u'\U000025B6',
  490.             'stop': u'\U000025FC'
  491.         }
  492.         self.emojis.update(telegramEmojiDict)
  493.     # all emojis will be get via this method to disable them globaly by the corrosponding setting  
  494.     # so if you want to use emojis anywhere use gEmo("...") istead of emojis["..."]
  495.     def gEmo(self,key):
  496.         if self._settings.get(["send_icon"]) and key in self.emojis:
  497.             return self.emojis[key]
  498.         return ""
  499.  
  500.     # starts the telegram listener thread
  501.     def start_listening(self):
  502.         if self._settings.get(['token']) != "" and self.thread is None:
  503.             self._logger.debug("Starting listener.")
  504.             self.bot_url = "https://api.telegram.org/bot" + self._settings.get(['token'])
  505.             self.bot_file_url = "https://api.telegram.org/file/bot" + self._settings.get(['token'])
  506.             self.thread = TelegramListener(self)
  507.             self.thread.daemon = True
  508.             self.thread.start()
  509.    
  510.     # stops the telegram listener thread
  511.     def stop_listening(self):
  512.         if self.thread is not None:
  513.             self._logger.debug("Stopping listener.")
  514.             self.thread.stop()
  515.             self.thread = None
  516.    
  517.     def shutdown(self):
  518.         self._logger.warn("shutdown() running!")
  519.         self.stop_listening()
  520.         self.send_messages = False
  521.    
  522.     def sending_okay(self):
  523.         # If the count ever goeas above 10, we stop doing everything else and just return False
  524.         # so if this is ever reached, it will stay this way.
  525.         if self.sending_okay_count > 10:
  526.             self._logger.warn("Sent more than 10 messages in the last minute. Shutting down...")
  527.             self.shutdown()
  528.             return False
  529.        
  530.         if self.sending_okay_minute != datetime.datetime.now().minute:
  531.             self.sending_okay_minute = datetime.datetime.now().minute
  532.             self.sending_okay_count = 1
  533.         else:
  534.             self.sending_okay_count += 1
  535.            
  536.         return True
  537.  
  538. ##########
  539. ### Asset API
  540. ##########
  541.  
  542.     def get_assets(self):
  543.         return dict(js=["js/telegram.js"])
  544.  
  545. ##########
  546. ### Template API
  547. ##########
  548.  
  549.     def get_template_configs(self):
  550.         return [
  551.             dict(type="settings", name="Telegram", custom_bindings=True)
  552.         ]
  553.  
  554. ##########
  555. ### Wizard API
  556. ##########
  557.  
  558.     def is_wizard_required(self):
  559.         return self._settings.get(["token"]) is ""
  560.  
  561.     def get_wizard_version(self):
  562.         return 1
  563.         # Wizard version numbers used in releases
  564.         # < 1.4.2 : no wizard
  565.         # 1.4.2 : 1
  566.         # 1.4.3 : 1
  567.  
  568. ##########
  569. ### Startup/Shutdown API
  570. ##########
  571.  
  572.     def on_after_startup(self):
  573.         self.set_log_level()
  574.         self._logger.addFilter(TelegramPluginLoggingFilter())
  575.         self.tcmd = TCMD(self)
  576.         self.tmsg = TMSG(self) # Notification Message Handler class. called only by on_event()
  577.         # initial settings for new chat.
  578.         # !!! sync this dict with newUsrDict in on_settings_migrate() !!!
  579.         self.newChat = {
  580.             'private': True,
  581.             'title': "[UNKNOWN]",
  582.             'accept_commands' : False,
  583.             'send_notifications' : False,
  584.             'new': True,
  585.             'type': '',
  586.             'allow_users': False,
  587.             'commands': {k: False for k,v in self.tcmd.commandDict.iteritems()},
  588.             'notifications': {k: False for k,v in telegramMsgDict.iteritems()}
  589.             }
  590.         self.chats = self._settings.get(["chats"])
  591.         self.start_listening()
  592.         self.track_action("started")
  593.         # Delete user profile photos if user doesn't exist anymore
  594.         for f in os.listdir(self.get_plugin_data_folder()+"/img/user"):
  595.             fcut = f.split('.')[0][3:]
  596.             self._logger.debug("Testing Pic ID " + str(fcut))
  597.             if fcut not in self.chats:
  598.                 self._logger.debug("Removing pic" +fcut+".jpg")
  599.                 try:
  600.                     os.remove(self.get_plugin_data_folder()+"/img/user/"+f)
  601.                 except OSError:
  602.                     pass
  603.         #Update user profile photos
  604.         for key in self.chats:
  605.             try:
  606.                 if key is not 'zBOTTOMOFCHATS':
  607.                     kwargs = {}
  608.                     kwargs['chat_id'] = int(key)
  609.                     t = threading.Thread(target=self.get_usrPic, kwargs=kwargs)
  610.                     t.daemon = True
  611.                     t.run()
  612.             except Exception:
  613.                 pass
  614.    
  615.     def on_shutdown(self):
  616.         self.on_event("PrinterShutdown",{})
  617.         self.stop_listening()
  618.  
  619. ##########
  620. ### Settings API
  621. ##########
  622.  
  623.     def get_settings_version(self):
  624.         return 4
  625.         # Settings version numbers used in releases
  626.         # < 1.3.0: no settings versioning
  627.         # 1.3.0 : 1
  628.         # 1.3.1 : 2
  629.         # 1.3.2 : 2
  630.         # 1.3.3 : 2
  631.         # 1.4.0 : 3
  632.         # 1.4.1 : 3
  633.         # 1.4.2 : 3
  634.         # 1.4.3 : 4
  635.  
  636.     def get_settings_defaults(self):
  637.         return dict(
  638.             token = "",
  639.             notification_height = 5.0,
  640.             notification_time = 15,
  641.             message_at_print_done_delay = 0,
  642.             messages = telegramMsgDict,
  643.             tracking_activated = False,
  644.             tracking_token = None,
  645.             chats = {'zBOTTOMOFCHATS':{'send_notifications': False,'accept_commands':False,'private':False}},
  646.             debug = False,
  647.             send_icon = True,
  648.             image_not_connected = True,
  649.             gif_not_connected = True, #GWE 05/05/19
  650.             fileOrder = False
  651.         )
  652.  
  653.     def get_settings_preprocessors(self):
  654.         return dict(), dict(
  655.             notification_height=lambda x: float(x),
  656.             notification_time=lambda x: int(x)
  657.         )
  658.  
  659.     def on_settings_migrate(self, target, current=None):
  660.         self._logger.setLevel(logging.DEBUG)
  661.         self._logger.debug("MIGRATE DO")
  662.         tcmd = TCMD(self)
  663.         # initial settings for new chat.
  664.         # !!! sync this dict with newChat in on_after_startup() !!!
  665.         newUsrDict = {
  666.             'private': True,
  667.             'title': "[UNKNOWN]",
  668.             'accept_commands' : False,
  669.             'send_notifications' : False,
  670.             'new': False,
  671.             'type': '',
  672.             'allow_users': False,
  673.             'commands': {k: False for k,v in tcmd.commandDict.iteritems()},
  674.             'notifications': {k: False for k,v in telegramMsgDict.iteritems()}
  675.             }
  676.  
  677.         ##########
  678.         ### migrate from old plugin Versions < 1.3 (old versions had no settings version check)
  679.         ##########
  680.         chats = {k: v for k, v in self._settings.get(['chats']).iteritems() if k != 'zBOTTOMOFCHATS'}
  681.         self._logger.debug("LOADED CHATS: " + str(chats))
  682.         self._settings.set(['chats'], {})
  683.         if current is None or current < 1:
  684.             ########## Update Chats
  685.             # there shouldn't be any chats, but maybe somone had installed any test branch.
  686.             # Then we have to check if all needed settings are populated
  687.             for chat in chats:
  688.                 for setting in newUsrDict:
  689.                     if setting not in chats[chat]:
  690.                         if setting == "commands":
  691.                             chats[chat]['commands'] = {k: False for k,v in tcmd.commandDict.iteritems() if 'bind_none' not in v}
  692.                         elif setting == "notifications":
  693.                             chats[chat]['notifications'] = {k: False for k,v in telegramMsgDict.iteritems()}
  694.                         else:
  695.                             chats[chat][setting] = False
  696.             ########## Is there a chat from old single user plugin version?
  697.             # then migrate it into chats
  698.             chat = self._settings.get(["chat"])
  699.             if chat is not None:
  700.                 self._settings.set(["chat"], None)
  701.                 data = {}
  702.                 data.update(newUsrDict)
  703.                 data['private'] = True
  704.                 data['title'] = "[UNKNOWN]"
  705.                 #try to get infos from telegram by sending a "you are migrated" message
  706.                 try:
  707.                     message = {}
  708.                     message['text'] = "The OctoPrint Plugin " + self._plugin_name + " has been updated to new Version "+self._plugin_version+ ".\n\nPlease open your " + self._plugin_name + " settings in OctoPrint and set configurations for this chat. Until then you are not able to send or receive anything useful with this Bot.\n\nMore informations on: https://github.com/fabianonline/OctoPrint-Telegram"
  709.                     message['chat_id'] = chat
  710.                     message['disable_web_page_preview'] = True
  711.                     r = requests.post("https://api.telegram.org/bot" + self._settings.get(['token']) + "/sendMessage", data =  message, proxies=self.getProxies())
  712.                     r.raise_for_status()
  713.                     if r.headers['content-type'] != 'application/json':
  714.                         raise Exception("invalid content-type")
  715.                     json = r.json()
  716.                     if not json['ok']:
  717.                         raise Exception("invalid request")
  718.                     chat = json['result']['chat']
  719.                     if chat['type']=='group':
  720.                         data['private'] = False
  721.                         data['title'] = chat['title']
  722.                     elif chat['type']=='private':
  723.                         data['private'] = True
  724.                         data['title'] = ""
  725.                         if "first_name" in chat:
  726.                             data['title'] += chat['first_name'] + " - "
  727.                         if "last_name" in chat:
  728.                             data['title'] += chat['last_name'] + " - "
  729.                         if "username" in chat:
  730.                             data['title'] += "@" + chat['username']
  731.                 except Exception as ex:
  732.                     self._logger.debug("ERROR migrating chat. Done with defaults private=true,title=[UNKNOWN] : " + str(ex))
  733.                 # place the migrated chat in chats
  734.                 chats.update({str(chat['id']): data})
  735.             self._logger.debug("MIGRATED Chats: " + str(chats))
  736.             ########## Update messages. Old text will be taken to new structure
  737.             messages = self._settings.get(['messages'])
  738.             msgOut = {}
  739.             for msg in messages:
  740.                 if msg == 'TelegramSendNotPrintingStatus':
  741.                     msg2 = 'StatusNotPrinting'
  742.                 elif msg == 'TelegramSendPrintingStatus':
  743.                     msg2 = 'StatusPrinting'
  744.                 else:
  745.                     msg2 = msg
  746.                 if type(messages[msg]) is not type({}):
  747.                     newMsg = telegramMsgDict[msg2].copy()
  748.                     newMsg['text'] = str(messages[msg])
  749.                     msgOut.update({msg2: newMsg})
  750.                 else:
  751.                     msgOut.update({msg2: messages[msg]})
  752.             self._settings.set(['messages'], msgOut)
  753.             ########## Delete old settings
  754.             self._settings.set(["message_at_startup"], None)
  755.             self._settings.set(["message_at_shutdown"], None)
  756.             self._settings.set(["message_at_print_started"], None)
  757.             self._settings.set(["message_at_print_done"], None)
  758.             self._settings.set(["message_at_print_failed"], None)
  759.  
  760.         ##########
  761.         ### Migrate to new command/notification settings version.
  762.         ### This should work on all future versions. So if you add/del
  763.         ### some commands/notifications, then increment settings version counter
  764.         ### in  get_settings_version(). This will trigger octoprint to update settings
  765.         ##########
  766.         if current is None or current < target:
  767.             # first we have to check if anything has changed in commandDict or telegramMsgDict
  768.             # then we have to update user comamnd or notification settings
  769.             if chats is not None and chats is not {}:
  770.                 # this for loop updates commands and notifications settings items of chats
  771.                 # if there are changes in commandDict or telegramMsgDict
  772.                 for chat in chats:
  773.                     # handle renamed commands
  774.                     if '/list' in chats[chat]['commands']:
  775.                         chats[chat]['commands'].update({'/files':chats[chat]['commands']['/list']})
  776.                     if '/imsorrydontshutup' in chats[chat]['commands']:
  777.                         chats[chat]['commands'].update({'/dontshutup':chats[chat]['commands']['/imsorrydontshutup']})
  778.                     if 'type' not in chats[chat]:
  779.                         chats[chat].update({'type': 'PRIVATE' if chats[chat]['private'] else 'GROUP'})
  780.                     delCmd = []
  781.                     # collect remove 'bind_none' commands
  782.                     for cmd in tcmd.commandDict:
  783.                         if cmd in chats[chat]['commands'] and 'bind_none' in tcmd.commandDict[cmd]:
  784.                             delCmd.append(cmd)
  785.                     # collect Delete commands from settings if they don't belong to commandDict anymore
  786.                     for cmd in chats[chat]['commands']:
  787.                         if cmd not in tcmd.commandDict:
  788.                             delCmd.append(cmd)
  789.                     # finally delete commands
  790.                     for cmd in delCmd:
  791.                         del chats[chat]['commands'][cmd]
  792.                     # If there are new commands in comamndDict, add them to settings
  793.                     for cmd in tcmd.commandDict:
  794.                         if cmd not in chats[chat]['commands']:
  795.                             if 'bind_none' not in tcmd.commandDict[cmd]:
  796.                                 chats[chat]['commands'].update({cmd: False})
  797.                     # Delete notifications from settings if they don't belong to msgDict anymore
  798.                     delMsg = []
  799.                     for msg in chats[chat]['notifications']:
  800.                         if msg not in telegramMsgDict:
  801.                             delMsg.append(msg)
  802.                     for msg in delMsg:
  803.                         del chats[chat]['notifications'][msg]
  804.                     # If there are new notifications in msgDict, add them to settings
  805.                     for msg in telegramMsgDict:
  806.                         if msg not in chats[chat]['notifications']:
  807.                             chats[chat]['notifications'].update({msg: False})
  808.                 self._settings.set(['chats'],chats)
  809.             ########## if anything changed in telegramMsgDict, we also have to update settings for messages
  810.             messages = self._settings.get(['messages'])
  811.             if messages is not None and messages is not {}:
  812.                 # this for loop deletes items from messages settings
  813.                 # if they dont't belong to telegramMsgDict anymore
  814.                 delMsg = []
  815.                 for msg in messages:
  816.                     if msg not in telegramMsgDict:
  817.                         delMsg.append(msg)
  818.                 for msg in delMsg:
  819.                     del messages[msg]
  820.                 # this for loop adds new message settings from telegramMsgDict to settings
  821.                 for msg in telegramMsgDict:
  822.                     if msg not in messages:
  823.                         messages.update({msg: telegramMsgDict[msg]})
  824.                     elif 'combined' not in messages[msg]:
  825.                         messages[msg].update({'combined' : True})
  826.  
  827.                 self._settings.set(['messages'],messages)
  828.                 self._logger.debug("MESSAGES: " + str(self._settings.get(['messages'])))
  829.  
  830.  
  831.         if current is not None:
  832.             if current < 2:
  833.                 if chats is not None and chats is not {}:
  834.                     for chat in chats:
  835.                         if os.path.isfile(self.get_plugin_data_folder()+"/pic"+chat+".jpg"):
  836.                             os.remove(self.get_plugin_data_folder()+"/pic"+chat+".jpg")
  837.  
  838.  
  839.         ##########
  840.         ### save the settings after Migration is done
  841.         ##########
  842.         self._logger.debug("SAVED Chats: " + str(self._settings.get(['chats'])))
  843.         try:
  844.             self._settings.save()
  845.         except Exception as ex:
  846.             self._logger.error("MIGRATED Save failed - " + str(ex))
  847.         self._logger.debug("MIGRATED Saved")
  848.  
  849.  
  850.     def on_settings_save(self, data):
  851.         # Remove 'new'-flag and apply bindings for all chats
  852.         if 'chats' in data and data['chats']:
  853.             delList = []
  854.             for key in data['chats']:
  855.                 if 'new' in data['chats'][key] or 'new' in data['chats'][key]:
  856.                     data['chats'][key]['new'] = False
  857.                 # Look for deleted chats
  858.                 if not key in self.chats and not key == "zBOTTOMOFCHATS":
  859.                     delList.append(key)
  860.             # Delete chats finally
  861.             for key in delList:
  862.                 del data['chats'][key]
  863.         # Also remove 'new'-flag from self.chats so settingsUI is consistent
  864.         # self.chats will only update to settings data on first received message after saving done
  865.         for key in self.chats:
  866.             if 'new' in self.chats[key]:
  867.                 self.chats[key]['new'] = False
  868.  
  869.         self._logger.debug("Saving data: " + str(data))
  870.         # Check token for right format
  871.         if 'token' in data:
  872.             data['token'] = data['token'].strip()
  873.             if not re.match("^[0-9]+:[a-zA-Z0-9_\-]+$", data['token']):
  874.                 self._logger.error("Not saving token because it doesn't seem to have the right format.")
  875.                 self.connection_state_str = gettext("The previously entered token doesn't seem to have the correct format. It should look like this: 12345678:AbCdEfGhIjKlMnOpZhGtDsrgkjkZTCHJKkzvjhb")
  876.                 data['token'] = ""
  877.         old_token = self._settings.get(["token"])
  878.         # Update Tracking
  879.         if 'tracking_activated' in data and not data['tracking_activated']:
  880.             data['tracking_token'] = None
  881.         # Now save settings
  882.         octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
  883.         self.set_log_level()
  884.         # Reconnect on new token
  885.         # Will stop listener on invalid token
  886.         if 'token' in data:
  887.             if data['token']!=old_token:
  888.                 self.stop_listening()
  889.             if data['token']!="":
  890.                 self.start_listening()
  891.             else:
  892.                 self.connection_state_str = gettext("No token given.")
  893.  
  894.     def on_settings_load(self):
  895.         data = octoprint.plugin.SettingsPlugin.on_settings_load(self)
  896.  
  897.         # only return our restricted settings to admin users - this is only needed for OctoPrint <= 1.2.16
  898.         restricted = (("token", None), ("tracking_token", None), ("chats", dict()))
  899.         for r, v in restricted:
  900.             if r in data and (current_user is None or current_user.is_anonymous() or not current_user.is_admin()):
  901.                 data[r] = v
  902.  
  903.         return data
  904.  
  905.     def get_settings_restricted_paths(self):
  906.         # only used in OctoPrint versions > 1.2.16
  907.         return dict(admin=[["token"], ["tracking_token"], ["chats"]])
  908.  
  909. ##########
  910. ### Softwareupdate API
  911. ##########
  912.    
  913.     def get_update_information(self, *args, **kwargs):
  914.         return dict(
  915.             telegram=dict(
  916.                 displayName=self._plugin_name,
  917.                 displayVersion=self._plugin_version,
  918.                 type="github_release",
  919.                 current=self._plugin_version,
  920.                 user="fabianonline",
  921.                 repo="OctoPrint-Telegram",
  922.                 pip="https://github.com/fabianonline/OctoPrint-Telegram/releases/{target_version}/download/release.zip"
  923.             )
  924.         )
  925.  
  926. ##########
  927. ### EventHandler API
  928. ##########
  929.        
  930.     def on_event(self, event, payload, **kwargs):
  931.         try:
  932.             # if we know the event, start handler
  933.             if event in self.tmsg.msgCmdDict:
  934.                 self._logger.debug("Got an event: " + event + " Payload: " + str(payload))
  935.                 # Start event handler
  936.                 self.tmsg.startEvent(event, payload, **kwargs)
  937.             else:
  938.                 # return as fast as possible
  939.                 return
  940.         except Exception as e:
  941.             self._logger.debug("Exception: " + str(e))
  942.  
  943. ##########
  944. ### SimpleApi API
  945. ##########
  946.  
  947.     def get_api_commands(self):
  948.         return dict(
  949.             testToken=["token"],
  950.             delChat=["ID"]
  951.         )
  952.  
  953.     def on_api_get(self, request):
  954.         # got an user-update with this command. so lets do that
  955.         if 'id' in request.args and 'cmd' in request.args and 'note' in request.args  and 'allow' in request.args:
  956.             self.chats[request.args['id']]['accept_commands'] = self.str2bool(str(request.args['cmd']))
  957.             self.chats[request.args['id']]['send_notifications'] = self.str2bool(str(request.args['note']))
  958.             self.chats[request.args['id']]['allow_users'] = self.str2bool(str(request.args['allow']))
  959.             self._logger.debug("Updated chat - " + str(request.args['id']))
  960.         elif 'bindings' in request.args:
  961.             bind_text = {}
  962.             for key in {k: v for k, v in telegramMsgDict.iteritems() if 'bind_msg' in v }:
  963.                 if telegramMsgDict[key]['bind_msg'] in bind_text:
  964.                     bind_text[telegramMsgDict[key]['bind_msg']].append(key)
  965.                 else:
  966.                     bind_text[telegramMsgDict[key]['bind_msg']] = [key]
  967.             return json.dumps({
  968.                 'bind_cmd':[k for k, v in self.tcmd.commandDict.iteritems() if 'bind_none' not in v ],
  969.                 'bind_msg':[k for k, v in telegramMsgDict.iteritems() if 'bind_msg' not in v ],
  970.                 'bind_text':bind_text,
  971.                 'no_setting':[k for k, v in telegramMsgDict.iteritems() if 'no_setting' in v ]})
  972.        
  973.         retChats = {k: v for k, v in self.chats.iteritems() if 'delMe' not in v and k != 'zBOTTOMOFCHATS'}
  974.         for chat in retChats:
  975.             if os.path.isfile(self.get_plugin_data_folder()+"/img/user/pic" +chat+".jpg"):
  976.                 retChats[chat]['image'] = "/plugin/telegram/img/user/pic" +chat+".jpg"
  977.             elif int(chat) < 0:
  978.                 retChats[chat]['image'] = "/plugin/telegram/img/static/group.jpg"
  979.             else:
  980.                 retChats[chat]['image'] = "/plugin/telegram/img/static/default.jpg"
  981.  
  982.         return json.dumps({'chats':retChats, 'connection_state_str':self.connection_state_str, 'connection_ok':self.connection_ok})
  983.    
  984.     def on_api_command(self, command, data):
  985.         if command=="testToken":
  986.             self._logger.debug("Testing token {}".format(data['token']))
  987.             try:
  988.                 if self._settings.get(["token"]) != data["token"]:
  989.                     username = self.test_token(data['token'])
  990.                     self._settings.set(['token'], data['token'])
  991.                     self.stop_listening() #to start with new token if already running
  992.                     self.start_listening()
  993.                     return json.dumps({'ok': True, 'connection_state_str': gettext("Token valid for %(username)s.", username=username), 'error_msg': None, 'username': username})
  994.                 return json.dumps({'ok': True, 'connection_state_str': gettext("Token valid for %(username)s.", username=self.thread.username), 'error_msg': None, 'username': self.thread.username})
  995.             except Exception as ex:
  996.                 return json.dumps({'ok': False, 'connection_state_str': gettext("Error: %(error)s", error=ex), 'username': None, 'error_msg': str(ex)})
  997.         # delete a chat (will not be removed and show up again on octorint restart
  998.         # if save button is not pressed on settings dialog)
  999.         elif command=="delChat":
  1000.             strId = str(data['ID'])
  1001.             if strId in self.chats:
  1002.                 del self.chats[strId]
  1003.                 # do self._settings.save() here???????
  1004.             return json.dumps({'chats':{k: v for k, v in self.chats.iteritems() if 'delMe' not in v and k != 'zBOTTOMOFCHATS'}, 'connection_state_str':self.connection_state_str, 'connection_ok':self.connection_ok})
  1005.  
  1006. ##########
  1007. ### Telegram API-Functions
  1008. ##########
  1009.  
  1010.     def send_msg(self, message, **kwargs):
  1011.         if not self.send_messages:
  1012.             return
  1013.            
  1014.         kwargs['message'] = message
  1015.         try:
  1016.             # If it's a regular event notification
  1017.             if 'chatID' not in kwargs and 'event' in kwargs:
  1018.                 self._logger.debug("Send_msg() found event: " + str(kwargs['event']))
  1019.                 for key in self.chats:
  1020.                     if key != 'zBOTTOMOFCHATS':
  1021.                         if self.chats[key]['notifications'][kwargs['event']] and (key not in self.shut_up or self.shut_up[key]==0) and self.chats[key]['send_notifications']:
  1022.                             kwargs['chatID'] = key
  1023.                             t = threading.Thread(target=self._send_msg, kwargs = kwargs).run()
  1024.             # Seems to be a broadcast
  1025.             elif 'chatID' not in kwargs:
  1026.                 for key in self.chats:
  1027.                     kwargs['chatID'] = key
  1028.                     t = threading.Thread(target=self._send_msg, kwargs = kwargs).run()
  1029.             # This is a 'editMessageText' message
  1030.             elif 'msg_id' in kwargs and kwargs['msg_id'] is not "" and  kwargs['msg_id'] is not None:
  1031.                 t = threading.Thread(target=self._send_edit_msg, kwargs = kwargs).run()
  1032.             # direct message or event notification to a chat_id
  1033.             else:
  1034.                 t = threading.Thread(target=self._send_msg, kwargs = kwargs).run()
  1035.         except Exception as ex:
  1036.             self._logger.debug("Caught an exception in send_msg(): " + str(ex))
  1037.  
  1038.  
  1039.     # this method is used to update a message text of a sent message
  1040.     # the sent message had to have no_markup = true when calling send_msg() (otherwise it would not work)
  1041.     # by setting no_markup = true we got a messageg_id on sending the message which is saved in selfupdateMessageID
  1042.     # if this message_id is passed in msg_id to send_msg() then this method will be called
  1043.     def _send_edit_msg(self,message="",msg_id="",chatID="", responses= None, inline=True, markup=None,delay=0, **kwargs):
  1044.         if not self.send_messages:
  1045.             return
  1046.            
  1047.         if delay > 0:
  1048.             time.sleep(delay)
  1049.         try:
  1050.             self._logger.debug("Sending a message UPDATE: " + message.replace("\n", "\\n") + " chatID= " + str(chatID))
  1051.             data = {}
  1052.             data['text'] = message
  1053.             data['message_id'] = msg_id
  1054.             data['chat_id'] = int(chatID)
  1055.             if markup is not None:
  1056.                 if "HTML" in markup  or "Markdown" in markup:
  1057.                     data["parse_mode"] = markup
  1058.             if responses and inline:
  1059.                 myArr = []
  1060.                 for k in responses:
  1061.                     myArr.append(map(lambda x: {"text":x[0],"callback_data":x[1]}, k))
  1062.                 keyboard = {'inline_keyboard':myArr}
  1063.                 data['reply_markup'] = json.dumps(keyboard)
  1064.             self._logger.debug("SENDING UPDATE: " + str(data))
  1065.             req = requests.post(self.bot_url + "/editMessageText", data=data, proxies=self.getProxies())
  1066.             if req.headers['content-type'] != 'application/json':
  1067.                 self._logger.debug(gettext("Unexpected Content-Type. Expected: application/json. Was: %(type)s. Waiting 2 minutes before trying again.", type=req.headers['content-type']))
  1068.                 return
  1069.             myJson = req.json()
  1070.             self._logger.debug("REQUEST RES: "+str(myJson))
  1071.             if inline:
  1072.                 self.updateMessageID[chatID] = msg_id
  1073.         except Exception as ex:
  1074.             self._logger.debug("Caught an exception in _send_edit_msg(): " + str(ex))
  1075.  
  1076.     def _send_msg(self, message="", with_image=False,with_gif=False,responses=None, delay=0, inline = True, chatID = "", markup=None, showWeb=False, **kwargs):
  1077.         if not self.send_messages:
  1078.             return
  1079.            
  1080.         if delay > 0:
  1081.             time.sleep(delay)
  1082.         try:
  1083.             if with_image:
  1084.                 if 'event' in kwargs and not self._settings.get(["messages",kwargs['event'],"combined"]):
  1085.                     args = locals()
  1086.                     del args['kwargs']['event']
  1087.                     del args['self']
  1088.                     args['message'] = ""
  1089.                     self._logger.debug("Sending image...")
  1090.                     t = threading.Thread(target=self._send_msg, kwargs = args).run()
  1091.                     args['message'] = message
  1092.                     args['with_image'] = False
  1093.                     self._logger.debug("Sending text...")
  1094.                     t = threading.Thread(target=self._send_msg, kwargs = args).run()
  1095.                     return
  1096.  
  1097.             self._logger.info("Sending a message: " + message.replace("\n", "\\n") + " with_image=" + str(with_image) + " with_gif=" + str(with_gif) + " chatID= " + str(chatID))
  1098.             data = {}
  1099.             # Do we want to show web link previews?
  1100.             data['disable_web_page_preview'] = not showWeb  
  1101.             # Do we want the message to be parsed in any markup?
  1102.             if markup is not None:
  1103.                 if "HTML" in markup  or "Markdown" in markup:
  1104.                     data["parse_mode"] = markup
  1105.             if responses:
  1106.                 myArr = []
  1107.                 for k in responses:
  1108.                     myArr.append(map(lambda x: {"text":x[0],"callback_data":x[1]}, k))
  1109.                 keyboard = {'inline_keyboard':myArr}
  1110.                 data['reply_markup'] = json.dumps(keyboard)
  1111.                
  1112.             image_data = None
  1113.             if with_image:
  1114.                 try:
  1115.                     image_data = self.take_image()
  1116.                 except Exception as ex:
  1117.                     self._logger.info("Caught an exception trying take image: " + str(ex))
  1118.  
  1119.             self._logger.debug("data so far: " + str(data))
  1120.  
  1121.             if not image_data and with_image:
  1122.                 message = "[ERR GET IMAGE]\n\n" + message
  1123.  
  1124.             r = None
  1125.             data['chat_id'] = chatID
  1126.             if image_data:
  1127.                 self._logger.debug("Sending with image.. " + str(chatID))
  1128.                 files = {'photo':("image.jpg", image_data)}
  1129.                 if message is not "":
  1130.                     data['caption'] = message
  1131.                 r = requests.post(self.bot_url + "/sendPhoto", files=files, data=data, proxies=self.getProxies())
  1132.                 self._logger.debug("Sending finished. " + str(r))
  1133.             else:
  1134.                 self._logger.debug("Sending without image.. " + str(chatID))
  1135.                 data['text'] = message
  1136.                 r =requests.post(self.bot_url + "/sendMessage", data=data, proxies=self.getProxies())
  1137.                 self._logger.debug("Sending finished. " + str(r.status_code))
  1138.  
  1139.             if r is not None and inline:
  1140.                 r.raise_for_status()
  1141.                 myJson = r.json()
  1142.                 if not myJson['ok']:
  1143.                     raise NameError("ReqErr")
  1144.                 if 'message_id' in myJson['result']:
  1145.                     self.updateMessageID[chatID] = myJson['result']['message_id']
  1146.  
  1147.             if with_gif : #giloser 05/05/19
  1148.                 try:
  1149.                     self._logger.info("Will try to create a gif ")
  1150.                     ret = self.create_gif()
  1151.                     if ret == 0:
  1152.                         self.send_file(chatID, self.get_plugin_data_folder()+"/tmpgif/gif.mp4")
  1153.                     else:
  1154.                         self.send_msg(self.gEmo('warning') + gettext(" Error trying to create gif, please be sure you have install avconv with command : "),chatID=chat_id,inline=False,with_image=False)
  1155.                     #self.send_video(chatID, video)
  1156.                 except Exception as ex:
  1157.                     self._logger.info("Caught an exception trying send gif: " + str(ex))
  1158.                     self.main.send_msg(self.gEmo('dizzy face') + " Problem creating gif, please check log file, and make sure you have installed libav-tools with command : `sudo apt-get install libav-tools`",chatID=chat_id)
  1159.  
  1160.  
  1161.         except Exception as ex:
  1162.             self._logger.info("Caught an exception in _send_msg(): " + str(ex))
  1163.    
  1164.     def send_file(self,chat_id,path):
  1165.         if not self.send_messages:
  1166.             return
  1167.            
  1168.         try:
  1169.             requests.get(self.bot_url + "/sendChatAction", params = {'chat_id': chat_id, 'action': 'upload_document'})
  1170.             files = {'document': open(path, 'rb')}
  1171.             r = requests.post(self.bot_url + "/sendDocument", files=files, data={'chat_id':chat_id}, proxies=self.getProxies())
  1172.         except Exception as ex:
  1173.             pass
  1174.  
  1175.     def send_video(self, message, video_file):
  1176.         if not self.send_messages:
  1177.             return
  1178.            
  1179.         files = {'video': open(video_file, 'rb')}
  1180.         #r = requests.post(self.bot_url + "/sendVideo", files=files, data={'chat_id':self._settings.get(["chat"]), 'caption':message})
  1181.         self._logger.debug("Sending finished. " + str(r.status_code) + " " + str(r.content))
  1182.    
  1183.     def get_file(self, file_id):
  1184.         if not self.send_messages:
  1185.             return
  1186.            
  1187.         self._logger.debug("Requesting file with id %s.", file_id)
  1188.         r = requests.get(self.bot_url + "/getFile", data={'file_id': file_id}, proxies=self.getProxies())
  1189.         # {"ok":true,"result":{"file_id":"BQADAgADCgADrWJxCW_eFdzxDPpQAg","file_size":26,"file_path":"document\/file_3.gcode"}}
  1190.         r.raise_for_status()
  1191.         data = r.json()
  1192.         if not "ok" in data:
  1193.             raise Exception(_("Telegram didn't respond well to getFile. The response was: %(response)s", response=r.text))
  1194.         url = self.bot_file_url + "/" + data['result']['file_path']
  1195.         self._logger.debug("Downloading file: %s", url)
  1196.         r = requests.get(url, proxies=self.getProxies())
  1197.         r.raise_for_status()
  1198.         return r.content
  1199.  
  1200.     def get_usrPic(self,chat_id, file_id=""):
  1201.         if not self.send_messages:
  1202.             return
  1203.            
  1204.         self._logger.debug("Requesting Profile Photo for chat_id: " + str(chat_id))
  1205.         try:
  1206.             if file_id == "":
  1207.                 if int(chat_id) < 0:
  1208.                     self._logger.debug("Not able to load group photos. "+ str(chat_id)+" EXIT")
  1209.                     return
  1210.                 r = requests.get(self.bot_url + "/getUserProfilePhotos", params = {'limit': 1, "user_id": chat_id}, proxies=self.getProxies())
  1211.                 r.raise_for_status()
  1212.                 data = r.json()
  1213.                 if not "ok" in data:
  1214.                     raise Exception(_("Telegram didn't respond well to getUserProfilePhoto "+ str(chat_id)+". The response was: %(response)s", response=r.text))
  1215.                 if data['result']['total_count'] < 1:
  1216.                     self._logger.debug("NO PHOTOS "+ str(chat_id)+". EXIT")
  1217.                     return
  1218.                 r = self.get_file(data['result']['photos'][0][0]['file_id'])
  1219.             else:
  1220.                 r = self.get_file(file_id)
  1221.             file_name = self.get_plugin_data_folder() + "/img/user/pic" + str(chat_id) + ".jpg"
  1222.             img = Image.open(StringIO.StringIO(r))
  1223.             img = img.resize((40, 40), PIL.Image.ANTIALIAS)
  1224.             img.save(file_name, format="JPEG")
  1225.             self._logger.debug("Saved Photo "+ str(chat_id))
  1226.  
  1227.         except Exception as ex:
  1228.             self._logger.error("Can't load UserImage: " + str(ex))
  1229.    
  1230.     def test_token(self, token=None):
  1231.         if not self.send_messages:
  1232.             return
  1233.            
  1234.         if token is None:
  1235.             token = self._settings.get(["token"])
  1236.         response = requests.get("https://api.telegram.org/bot" + token + "/getMe", proxies=self.getProxies())
  1237.         self._logger.debug("getMe returned: " + str(response.json()))
  1238.         self._logger.debug("getMe status code: " + str(response.status_code))
  1239.         json = response.json()
  1240.         if not 'ok' in json or not json['ok']:
  1241.             if json['description']:
  1242.                 raise(Exception(gettext("Telegram returned error code %(error)s: %(message)s", error=json['error_code'], message=json['description'])))
  1243.             else:
  1244.                 raise(Exception(gettext("Telegram returned an unspecified error.")))
  1245.         else:
  1246.             return "@" + json['result']['username']
  1247.  
  1248. ##########
  1249. ### Helper methods
  1250. ##########
  1251.  
  1252.     def str2bool(self,v):
  1253.         return v.lower() in ("yes", "true", "t", "1")
  1254.  
  1255.     def set_log_level(self):
  1256.         self._logger.setLevel(logging.DEBUG if self._settings.get_boolean(["debug"]) else logging.NOTSET)
  1257.  
  1258.     def getProxies(self):
  1259.         http_proxy = self._settings.get(["http_proxy"])
  1260.         https_proxy = self._settings.get(["https_proxy"])
  1261.         return {
  1262.             'http': http_proxy,
  1263.             'https': https_proxy
  1264.             }
  1265.  
  1266. # checks if the received command is allowed to execute by the user
  1267.     def isCommandAllowed(self, chat_id, from_id, command):
  1268.         if 'bind_none' in self.tcmd.commandDict[command]:
  1269.             return True
  1270.         if command is not None or command is not "":
  1271.             if self.chats[chat_id]['accept_commands']:
  1272.                 if self.chats[chat_id]['commands'][command]:
  1273.                         return True
  1274.                 elif int(chat_id) < 0 and self.chats[chat_id]['allow_users']:
  1275.                     if self.chats[from_id]['commands'][command] and self.chats[from_id]['accept_commands']:
  1276.                         return True
  1277.             elif int(chat_id) < 0 and self.chats[chat_id]['allow_users']:
  1278.                 if self.chats[from_id]['commands'][command] and self.chats[from_id]['accept_commands']:
  1279.                         return True
  1280.         return False
  1281.  
  1282.     # Helper function to handle /editMessageText Telegram API commands
  1283.     # see main._send_edit_msg()
  1284.     def getUpdateMsgId(self,id):
  1285.         uMsgID = ""
  1286.         if id in self.updateMessageID:
  1287.             uMsgID = self.updateMessageID[id]
  1288.             del self.updateMessageID[id]
  1289.         return uMsgID
  1290.  
  1291.     def take_image(self):
  1292.         snapshot_url = self._settings.global_get(["webcam", "snapshot"])
  1293.         self._logger.debug("Snapshot URL: " + str(snapshot_url))
  1294.         data = None
  1295.         if snapshot_url:
  1296.             try:
  1297.                 r = requests.get(snapshot_url)
  1298.                 data = r.content
  1299.             except Exception as e:
  1300.                 return None
  1301.         flipH = self._settings.global_get(["webcam", "flipH"])
  1302.         flipV = self._settings.global_get(["webcam", "flipV"])
  1303.         rotate= self._settings.global_get(["webcam", "rotate90"])
  1304.         self._logger.debug("Image transformations [H:%s, V:%s, R:%s]", flipH, flipV, rotate)
  1305.         if flipH or flipV or rotate:
  1306.             image = Image.open(StringIO.StringIO(data))
  1307.             if flipH:
  1308.                 image = image.transpose(Image.FLIP_LEFT_RIGHT)
  1309.             if flipV:
  1310.                 image = image.transpose(Image.FLIP_TOP_BOTTOM)
  1311.             if rotate:
  1312.                 image = image.transpose(Image.ROTATE_270)
  1313.             output = StringIO.StringIO()
  1314.             image.save(output, format="JPEG")
  1315.             data = output.getvalue()
  1316.             output.close()
  1317.         return data
  1318.        
  1319.  
  1320.     def calculate_ETA(self,printTime = 0):
  1321.         try:
  1322.             strtime = ""
  1323.             strdate = ""
  1324.             currentData = self._printer.get_current_data()
  1325.             current_time = datetime.datetime.today()
  1326.             if not currentData["progress"]["printTimeLeft"]:
  1327.                 if not printTime == 0:
  1328.                     finish_time = current_time + datetime.timedelta(0,printTime)
  1329.                 else:
  1330.                     return ""
  1331.             else:
  1332.                 finish_time = current_time + datetime.timedelta(0,currentData["progress"]["printTimeLeft"])
  1333.             strtime = format_time(finish_time)
  1334.             strdate = ""
  1335.             if finish_time.day > current_time.day:
  1336.                 if finish_time.day == current_time.day + 1:
  1337.                     strdate = " Tomorrow"
  1338.                 else:
  1339.                     strtime = " " + format_date(finish_time,"EEE d")
  1340.         except Exception as ex:
  1341.             self._logger.info("An Exception in get final time : " + str(ex) )
  1342.  
  1343.         return strtime + strdate
  1344.  
  1345.     def create_gif(self,nbImg = 20):  #giloser 05/05/2019
  1346.         i=0
  1347.         ret = 0
  1348.         try:
  1349.             saveDir = os.getcwd()
  1350.             os.chdir(self.get_plugin_data_folder()+"/tmpgif")
  1351.             try:
  1352.                 #self._file_manager.remove_folder(self.get_plugin_data_folder() , "/tmpgif", recursive=True)
  1353.                 list_files = glob.glob('Gif_Telegram_*.jpg')
  1354.                 for filename in list_files:
  1355.                     os.remove(filename)
  1356.                 os.remove(self.get_plugin_data_folder()+"/tmpgif/gif.mp4")
  1357.             except Exception as ex:
  1358.                 self._logger.info("Caught an exception trying clean previous images : " + str(ex))
  1359.             self._logger.info("will try to save image in path " + os.getcwd())
  1360.             #try:
  1361.             #   self._file_manager.add_folder(self.get_plugin_data_folder() , "/tmpgif", ignore_existing=True)
  1362.             #except Exception as ex:
  1363.             #   self._logger.info("Caught an exception trying create tmpgif folder : " + str(ex))
  1364.             while(i<nbImg):
  1365.                 data = self.take_image()
  1366.                 try:
  1367.                     #self._file_manager.add_file(self.get_plugin_data_folder() + "/tmpgif",'Test_Telegram_%02d.jpg' % i,data,allow_overwrite=True)
  1368.                     image = Image.open(StringIO.StringIO(data))
  1369.                     image.save('Gif_Telegram_%02d.jpg' % i, 'JPEG')
  1370.                 except Exception as ex:
  1371.                     self._logger.info("Caught an exception trying create gif() in loop as open of save image : " + str(ex))
  1372.                     ret = -2
  1373.                 time.sleep(.5) #giloser 19/05/2019 add sleep to better gif
  1374.                 i+=1
  1375.             try:
  1376.                 subprocess.check_call(['ffmpeg', '-r', '10', '-y', '-i' ,self.get_plugin_data_folder() + '/tmpgif/Gif_Telegram_%2d.jpg', '-crf', '20', '-g' ,'15', self.get_plugin_data_folder() + '/tmpgif/gif.mp4'])
  1377.                 #subprocess.check_call(['avconv','-r', '3','-y', '-i' ,'Test_Telegram_%02d.jpg','-vcodec', 'libx264', '-vf','scale=1280:720','timelapse.mp4'])
  1378.                 #subprocess.check_call(['avconv','-r', '3','-y', '-i' ,'Test_Telegram_%02d.jpg','-vcodec', 'libx264', '-vf', 'scale=1280:720','timelapse.mp4'])
  1379.             except Exception as ex:
  1380.                 self._logger.info("Caught an exception trying create mp4 : " + str(ex))
  1381.                 try:
  1382. #                   subprocess.call(['avconv','-r 3','-y','-i','Test_Telegram_%02d.jpg','timelapse.mp4'])
  1383.                     subprocess.call(['ffmpeg','-r 10','-y','-i',self.get_plugin_data_folder() + '/tmpgif/Gif_Telegram_%02d.jpg',self.get_plugin_data_folder() + '/tmpgif/gif.mp4'])
  1384.                 except Exception as ex:
  1385.                     self._logger.info("Caught an exception trying create mp4 2 : " + str(ex))
  1386.                     ret = -1
  1387.         #subprocess.call(['avconv -r 3 -y -i Test_Telegram_%02d.jpg -r 3 -vcodec libx264 -vf  scale=1280:720 timelapse.mp4'])
  1388.         #avconv -r 3 -y -i Test_Telegram_%02d.jpg -r 3 -vcodec libx264 -vf  scale=1280:720 timelapse.mp4
  1389.         except Exception as ex:
  1390.             self._logger.info("Caught an exception trying create gif general error : " + str(ex))
  1391.             ret = -3
  1392.         os.chdir(saveDir)
  1393.         return ret
  1394.  
  1395.  
  1396.     def track_action(self, action):
  1397.         if not self._settings.get_boolean(["tracking_activated"]):
  1398.             return
  1399.         if self._settings.get(["tracking_token"]) is None:
  1400.             token = "".join(random.choice("abcdef0123456789") for i in xrange(16))
  1401.             self._settings.set(["tracking_token"], token)
  1402.         params = {
  1403.             'idsite': '3',
  1404.             'rec': '1',
  1405.             'url': 'http://octoprint-telegram/'+action,
  1406.             'action_name': ("%20/%20".join(action.split("/"))),
  1407.             '_id': self._settings.get(["tracking_token"]),
  1408.             'uid': self._settings.get(["tracking_token"]),
  1409.             'cid': self._settings.get(["tracking_token"]),
  1410.             'send_image': '0',
  1411.             '_idvc': '1',
  1412.             'dimension1': str(self._plugin_version)
  1413.         }
  1414.         t = threading.Thread(target=requests.get, args=("http://piwik.schlenz.ruhr/piwik.php",), kwargs={'params': params})
  1415.         t.daemon = True
  1416.         t.run()
  1417.  
  1418.     def route_hook(self, server_routes, *args, **kwargs):
  1419.         from octoprint.server.util.tornado import LargeResponseHandler, UrlProxyHandler, path_validation_factory
  1420.         from octoprint.util import is_hidden_path
  1421.         if not os.path.exists(self.get_plugin_data_folder()+"/img"):
  1422.             os.mkdir(self.get_plugin_data_folder()+"/img")
  1423.         if not os.path.exists(self.get_plugin_data_folder()+"/img/user"):
  1424.             os.mkdir(self.get_plugin_data_folder()+"/img/user")
  1425.         if not os.path.exists(self.get_plugin_data_folder()+"/tmpgif"): #GWE 05/05/2019 add a folder temp to put image used in gif
  1426.             os.mkdir(self.get_plugin_data_folder()+"/tmpgif")
  1427.         if not os.path.exists(self.get_plugin_data_folder()+"/tmpzip"): #GWE 05/05/2019 add a folder temp to put image used in gif
  1428.             os.mkdir(self.get_plugin_data_folder()+"/tmpzip")
  1429.         return [
  1430.                 (r"/img/user/(.*)", LargeResponseHandler, dict(path=self.get_plugin_data_folder() + r"/img/user/", as_attachment=True,allow_client_caching =False)),
  1431.                 (r"/img/static/(.*)", LargeResponseHandler, dict(path=self._basefolder + "/static/img/", as_attachment=True,allow_client_caching =True))
  1432.                 ]
  1433.  
  1434. ########################################
  1435. ########################################
  1436. ### Some methods to check version and
  1437. ### get the right implementation
  1438. ########################################
  1439. ########################################
  1440.  
  1441. # copied from pluginmanager plugin
  1442. def _is_octoprint_compatible(compatibility_entries):
  1443.     """
  1444.     Tests if the current octoprint_version is compatible to any of the provided ``compatibility_entries``.
  1445.     """
  1446.  
  1447.     octoprint_version = _get_octoprint_version()
  1448.     for octo_compat in compatibility_entries:
  1449.         if not any(octo_compat.startswith(c) for c in ("<", "<=", "!=", "==", ">=", ">", "~=", "===")):
  1450.             octo_compat = ">={}".format(octo_compat)
  1451.  
  1452.         s = next(pkg_resources.parse_requirements("OctoPrint" + octo_compat))
  1453.         if octoprint_version in s:
  1454.             break
  1455.     else:
  1456.         return False
  1457.  
  1458.     return True
  1459.  
  1460. # copied from pluginmanager plugin
  1461. def _get_octoprint_version():
  1462.     from octoprint.server import VERSION
  1463.     octoprint_version_string = VERSION
  1464.  
  1465.     if "-" in octoprint_version_string:
  1466.         octoprint_version_string = octoprint_version_string[:octoprint_version_string.find("-")]
  1467.  
  1468.     octoprint_version = pkg_resources.parse_version(octoprint_version_string)
  1469.     if isinstance(octoprint_version, tuple):
  1470.         # old setuptools
  1471.         base_version = []
  1472.         for part in octoprint_version:
  1473.             if part.startswith("*"):
  1474.                 break
  1475.             base_version.append(part)
  1476.         octoprint_version = ".".join(base_version)
  1477.     else:
  1478.         # new setuptools
  1479.         octoprint_version = pkg_resources.parse_version(octoprint_version.base_version)
  1480.  
  1481.     return octoprint_version
  1482. # check if we have min version 1.3.0
  1483. # this is important because of WizardPlugin mixin and folders in filebrowser
  1484. def get_implementation_class():
  1485.     if not _is_octoprint_compatible(["1.3.0"]):
  1486.         return TelegramPlugin(1.2)
  1487.     else:
  1488.         class NewTelegramPlugin(TelegramPlugin,octoprint.plugin.WizardPlugin):
  1489.             def __init__(self,version):
  1490.                 super(self.__class__, self).__init__(version)
  1491.         return NewTelegramPlugin(1.3)
  1492.  
  1493.        
  1494. __plugin_name__ = "Telegram Notifications"
  1495. __plugin_implementation__ = get_implementation_class()
  1496. __plugin_hooks__ = {
  1497.     "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
  1498.     "octoprint.server.http.routes": __plugin_implementation__.route_hook
  1499. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top