Files
gajim-plugins/url_shortener/url_shortener.py
Philipp Hörist 9ce1c5b961 Use absolute imports on all plugins
This is necessary because Gajim is with 0.16.11 a python package
2017-08-18 21:35:29 +02:00

233 lines
9.6 KiB
Python

# -*- coding: utf-8 -*-
import gtk
import json
import urllib
import urllib2
from common import app
from common import ged
from plugins import GajimPlugin
from plugins.helpers import log_calls
from plugins.gui import GajimPluginConfigDialog
APIKEY = 'R_fcba926fc7978bd19acbca73ec82b2be'
USER = 'dicson'
class UrlShortenerPlugin(GajimPlugin):
@log_calls('UrlShortenerPlugin')
def init(self):
self.description = _('Plugin that allows users to shorten a long URL '
'in received messages.\n'
'For example, you can turn this link:\n'
'https://trac.gajim.org/timeline\n'
'Into this link:\n'
'http://bit.ly/THy6ZK')
self.config_dialog = UrlShortenerPluginConfigDialog(self)
self.gui_extension_points = {
'chat_control_base': (self.connect_with_chat_control,
self.disconnect_from_chat_control),
'print_special_text': (self.print_special_text,
self.print_special_text1),}
self.config_default_values = {
'MAX_CHARS': (50, ('MAX_CHARS(30-...)')),
'IN_MAX_CHARS': (50, ('MAX_CHARS(30-...)')),
'SHORTEN_OUTGOING': (False, ''),}
self.events_handlers = {'message-outgoing': (ged.OUT_PRECORE,
self.handle_outgoing_msg),
'gc-message-outgoing': (ged.OUT_PRECORE,
self.handle_outgoing_msg)}
self.chat_control = None
self.controls = []
def handle_outgoing_msg(self, event):
if not self.active:
return
if not event.message:
return
if not self.config['SHORTEN_OUTGOING']:
return
if hasattr(event, 'shortened'):
return
iterator = app.interface.basic_pattern_re.finditer(event.message)
for match in iterator:
start, end = match.span()
link = event.message[start:end]
if len(link) < self.config['MAX_CHARS']:
continue
short_link = None
try:
params = urllib.urlencode({'longUrl': link,
'login': USER,
'apiKey': APIKEY,
'format': 'json'})
req = urllib2.Request('http://api.bit.ly/v3/shorten?%s' % params)
response = urllib2.urlopen(req)
j = json.load(response)
if j['status_code'] == 200:
short_link = j['data']['url']
except urllib2.HTTPError, e:
pass
if short_link:
event.message = event.message.replace(link, short_link)
event.callback_args[1] = event.message
event.shortened = True
@log_calls('UrlShortenerPlugin')
def connect_with_chat_control(self, chat_control):
self.chat_control = chat_control
control = Base(self, self.chat_control)
self.controls.append(control)
@log_calls('UrlShortenerPlugin')
def disconnect_from_chat_control(self, chat_control):
for control in self.controls:
control.disconnect_from_chat_control()
self.controls = []
def print_special_text(self, tv, special_text, other_tags, graphics=True,
additional_data={}):
for control in self.controls:
if control.chat_control.conv_textview != tv:
continue
control.print_special_text(special_text, other_tags, graphics=True)
def print_special_text1(self, chat_control, special_text, other_tags=None,
graphics=True, additional_data={}):
for control in self.controls:
if control.chat_control == chat_control:
control.disconnect_from_chat_control()
self.controls.remove(control)
class Base(object):
def __init__(self, plugin, chat_control):
self.plugin = plugin
self.chat_control = chat_control
self.textview = self.chat_control.conv_textview
self.id_ = self.textview.tv.connect('motion_notify_event',
self.on_textview_motion_notify_event)
self.chat_control.handlers[self.id_] = self.textview.tv
def print_special_text(self, special_text, other_tags, graphics=True):
if not self.plugin.active:
return
is_xhtml_link = None
text_is_valid_uri = False
buffer_ = self.textview.tv.get_buffer()
# Detect XHTML-IM link
ttt = buffer_.get_tag_table()
tags_ = [(ttt.lookup(t) if isinstance(t, str) else t) for t in other_tags]
for t in tags_:
is_xhtml_link = getattr(t, 'href', None)
if is_xhtml_link:
break
# Check if we accept this as an uri
schemes = app.config.get('uri_schemes').split()
for scheme in schemes:
if special_text.startswith(scheme):
text_is_valid_uri = True
if special_text.startswith('www.') or special_text.startswith('ftp.') \
or text_is_valid_uri and not is_xhtml_link:
if len(special_text) < self.plugin.config['IN_MAX_CHARS']:
return
end_iter = buffer_.get_end_iter()
mark = buffer_.create_mark(None, end_iter, True)
app.thread_interface(self.insert_hyperlink, [mark, special_text,
ttt])
self.textview.plugin_modified = True
def insert_hyperlink(self, mark, special_text, ttt):
try:
params = urllib.urlencode({'longUrl': special_text,
'login': USER,
'apiKey': APIKEY,
'format': 'json'})
req = urllib2.Request('http://api.bit.ly/v3/shorten?%s' % params)
response = urllib2.urlopen(req)
j = json.load(response)
if j['status_code'] == 200:
special_text = j['data']['url']
except urllib2.HTTPError, e:
pass
buffer_ = mark.get_buffer()
end_iter = buffer_.get_iter_at_mark(mark)
buffer_.insert_with_tags(end_iter, special_text, ttt.lookup('url'))
def on_textview_motion_notify_event(self, widget, event):
pointer_x, pointer_y = self.textview.tv.window.get_pointer()[0:2]
x, y = self.textview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
pointer_x, pointer_y)
tags = self.textview.tv.get_iter_at_location(x, y).get_tags()
tag_table = self.textview.tv.get_buffer().get_tag_table()
buffer_ = self.textview.tv.get_buffer()
for tag in tags:
if tag != tag_table.lookup('url'):
continue
it = self.textview.tv.get_iter_at_location(x, y)
st = it.copy()
st.backward_to_tag_toggle(tag_table.lookup('url'))
it.forward_to_tag_toggle(tag_table.lookup('url'))
text = buffer_.get_text(st, it, include_hidden_chars=True)
if text.startswith('http://bit.ly/'):
try:
params = urllib.urlencode({'shortUrl': text,
'login': USER,
'apiKey': APIKEY,
'format': 'json'})
req = urllib2.Request('http://api.bit.ly/v3/expand?%s' \
% params)
response = urllib2.urlopen(req)
j = json.load(response)
if j['status_code'] != 200:
raise Exception('%s'%j['status_txt'])
txt = j['data']['expand'][0]['long_url']
self.textview.tv.set_tooltip_text(txt)
self.textview.on_textview_motion_notify_event(widget, event)
return
except Exception, e:
break
self.textview.tv.set_tooltip_text('')
self.textview.on_textview_motion_notify_event(widget, event)
def disconnect_from_chat_control(self):
pass
class UrlShortenerPluginConfigDialog(GajimPluginConfigDialog):
def init(self):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
'config_dialog.ui')
self.xml = gtk.Builder()
self.xml.set_translation_domain('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['vbox1'])
self.max_chars_spinbutton = self.xml.get_object('max_chars')
self.max_chars_spinbutton.get_adjustment().set_all(30, 30, 99999, 1,
10, 0)
self.in_max_chars_spinbutton = self.xml.get_object('in_max_chars')
self.in_max_chars_spinbutton.get_adjustment().set_all(30, 30, 99999, 1,
10, 0)
self.shorten_outgoing = self.xml.get_object('shorten_outgoing')
hbox = self.xml.get_object('vbox1')
self.child.pack_start(hbox)
self.xml.connect_signals(self)
def on_run(self):
self.max_chars_spinbutton.set_value(self.plugin.config['MAX_CHARS'])
self.in_max_chars_spinbutton.set_value(self.plugin.config['IN_MAX_CHARS'])
self.shorten_outgoing.set_active(self.plugin.config['SHORTEN_OUTGOING'])
def avatar_size_value_changed(self, spinbutton):
self.plugin.config['MAX_CHARS'] = spinbutton.get_value()
def on_in_max_chars_value_changed(self, spinbutton):
self.plugin.config['IN_MAX_CHARS'] = spinbutton.get_value()
def shorten_outgoing_toggled(self, checkbutton):
self.plugin.config['SHORTEN_OUTGOING'] = checkbutton.get_active()