Added reading article support from Socializer wall posts
This commit is contained in:
parent
273f25c24f
commit
1f575f0311
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
### new additions
|
### new additions
|
||||||
|
|
||||||
|
* It is now possible to read an article from a wall post. The article will be opened in a new dialog. This might work better in countries where VK is blocked as users no longer need to open the web browser. Unfortunately, as articles are mainly undocumented in the API, it is not possible to perform other actions besides reading them from Socializer.
|
||||||
* the spelling correction module is able to add words to the dictionary so it will learn which words should start to ignore.
|
* the spelling correction module is able to add words to the dictionary so it will learn which words should start to ignore.
|
||||||
|
|
||||||
### bugfixes
|
### bugfixes
|
||||||
|
@ -218,6 +218,21 @@ class displayAudioInteractor(base.baseInteractor):
|
|||||||
post = self.view.get_audio()
|
post = self.view.get_audio()
|
||||||
self.presenter.remove_from_library(post)
|
self.presenter.remove_from_library(post)
|
||||||
|
|
||||||
|
class displayArticleInteractor(base.baseInteractor):
|
||||||
|
|
||||||
|
def set(self, control, value):
|
||||||
|
if not hasattr(self.view, control):
|
||||||
|
raise AttributeError("The control is not present in the view.")
|
||||||
|
getattr(self.view, control).SetValue(value)
|
||||||
|
|
||||||
|
def install(self, *args, **kwargs):
|
||||||
|
super(displayArticleInteractor, self).install(*args, **kwargs)
|
||||||
|
pub.subscribe(self.set, self.modulename+"_set")
|
||||||
|
|
||||||
|
def uninstall(self):
|
||||||
|
super(displayArticleInteractor, self).uninstall()
|
||||||
|
pub.unsubscribe(self.set, self.modulename+"_set")
|
||||||
|
|
||||||
class displayPollInteractor(base.baseInteractor):
|
class displayPollInteractor(base.baseInteractor):
|
||||||
|
|
||||||
def set(self, control, value):
|
def set(self, control, value):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from .basePost import *
|
from .basePost import *
|
||||||
from .audio import *
|
from .audio import *
|
||||||
|
from .article import *
|
||||||
from .comment import *
|
from .comment import *
|
||||||
from .peopleList import *
|
from .peopleList import *
|
||||||
from .poll import *
|
from .poll import *
|
||||||
|
47
src/presenters/displayPosts/article.py
Normal file
47
src/presenters/displayPosts/article.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Presenter to render an article from the VK mobile website.
|
||||||
|
this is an helper class to display an article within socializer, as opposed to opening a web browser and asking the user to get there.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from presenters import base
|
||||||
|
|
||||||
|
log = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
class displayArticlePresenter(base.basePresenter):
|
||||||
|
def __init__(self, session, postObject, view, interactor):
|
||||||
|
super(displayArticlePresenter, self).__init__(view=view, interactor=interactor, modulename="display_article")
|
||||||
|
self.session = session
|
||||||
|
self.post = postObject
|
||||||
|
self.load_article()
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
def load_article(self):
|
||||||
|
""" Loads the article in the interactor.
|
||||||
|
This function retrieves, by using the params defined in the VK API, the web version of the article and extracts some info from it.
|
||||||
|
this is needed because there are no public API for articles so far.
|
||||||
|
"""
|
||||||
|
article = self.post[0]
|
||||||
|
# By using the vk_api's session, proxy settings are applied, thus might work in blocked countries.
|
||||||
|
article_body = self.session.vk.session_object.http.get(article["view_url"])
|
||||||
|
# Parse and extract text from all paragraphs.
|
||||||
|
# ToDo: Extract all links and set those as attachments.
|
||||||
|
soup = BeautifulSoup(article_body.text, "lxml")
|
||||||
|
# ToDo: Article extraction require testing to see if you can add more tags beside paragraphs.
|
||||||
|
msg = [p.get_text() for p in soup.find_all("p")]
|
||||||
|
msg = "\n\n".join(msg)
|
||||||
|
self.send_message("set", control="article_view", value=msg)
|
||||||
|
self.send_message("set_title", value=article["title"])
|
||||||
|
# Retrieve views count
|
||||||
|
views = soup.find("div", class_="articleView__views_info")
|
||||||
|
# This might return None if VK changes anything, so let's avoid errors.
|
||||||
|
if views == None:
|
||||||
|
views = str(-1)
|
||||||
|
else:
|
||||||
|
views = views.text
|
||||||
|
# Find the integer and remove the words from the string.
|
||||||
|
numbers = re.findall(r'\d+', views)
|
||||||
|
if len(numbers) != 0:
|
||||||
|
views = numbers[0]
|
||||||
|
self.send_message("set", control="views", value=views)
|
@ -15,7 +15,7 @@ from extra import SpellChecker, translator
|
|||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from presenters import base
|
from presenters import base
|
||||||
from presenters.createPosts.basePost import createPostPresenter
|
from presenters.createPosts.basePost import createPostPresenter
|
||||||
from . import audio, poll
|
from . import audio, poll, article
|
||||||
|
|
||||||
log = logging.getLogger(__file__)
|
log = logging.getLogger(__file__)
|
||||||
|
|
||||||
@ -351,10 +351,9 @@ class displayPostPresenter(base.basePresenter):
|
|||||||
elif attachment["type"] == "poll":
|
elif attachment["type"] == "poll":
|
||||||
a = poll.displayPollPresenter(session=self.session, poll=attachment, interactor=interactors.displayPollInteractor(), view=views.displayPoll())
|
a = poll.displayPollPresenter(session=self.session, poll=attachment, interactor=interactors.displayPollInteractor(), view=views.displayPoll())
|
||||||
elif attachment["type"] == "article":
|
elif attachment["type"] == "article":
|
||||||
output.speak(_("Opening Article in web browser..."), True)
|
a = article.displayArticlePresenter(session=self.session, postObject=[attachment["article"]], interactor=interactors.displayArticleInteractor(), view=views.displayArticle())
|
||||||
webbrowser.open_new_tab(attachment["article"]["url"])
|
|
||||||
else:
|
else:
|
||||||
log.debug("Unhandled attachment: %r" % (attachment,))
|
log.error("Unhandled attachment: %r" % (attachment,))
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if hasattr(self, "worker"):
|
if hasattr(self, "worker"):
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" Session object for Socializer. A session is the only object to call VK API methods, save settings and access to the cache database and sound playback mechanisms. """
|
""" Session object for Socializer. A session is the only object to call VK API methods, save settings and access to the cache database and sound playback mechanisms. """
|
||||||
from __future__ import unicode_literals
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
|
|
||||||
@ -283,6 +282,76 @@ class displayAudio(widgetUtils.BaseDialog):
|
|||||||
def get_audio(self):
|
def get_audio(self):
|
||||||
return self.list.GetSelection()
|
return self.list.GetSelection()
|
||||||
|
|
||||||
|
class displayArticle(widgetUtils.BaseDialog):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(displayArticle, self).__init__(parent=None, *args, **kwargs)
|
||||||
|
self.panel = wx.Panel(self, -1)
|
||||||
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
article_view_box = self.create_article_view()
|
||||||
|
self.sizer.Add(article_view_box, 0, wx.ALL, 5)
|
||||||
|
views_box = self.create_views_control()
|
||||||
|
self.sizer.Add(views_box, 0, wx.ALL, 5)
|
||||||
|
attachments_box = self.create_attachments()
|
||||||
|
self.sizer.Add(attachments_box, 0, wx.ALL, 5)
|
||||||
|
self.attachments.list.Enable(False)
|
||||||
|
self.create_tools_button()
|
||||||
|
self.sizer.Add(self.tools, 0, wx.ALL, 5)
|
||||||
|
self.sizer.Add(self.create_dialog_buttons())
|
||||||
|
self.done()
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
self.panel.SetSizer(self.sizer)
|
||||||
|
self.SetClientSize(self.sizer.CalcMin())
|
||||||
|
|
||||||
|
def create_article_view(self, label=_("Article")):
|
||||||
|
lbl = wx.StaticText(self.panel, -1, label)
|
||||||
|
self.article_view = wx.TextCtrl(self.panel, -1, size=(730, -1), style=wx.TE_READONLY|wx.TE_MULTILINE|wx.BORDER_SIMPLE)
|
||||||
|
selectId = wx.NewId()
|
||||||
|
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||||
|
self.accel_tbl = wx.AcceleratorTable([
|
||||||
|
(wx.ACCEL_CTRL, ord('A'), selectId),])
|
||||||
|
self.SetAcceleratorTable(self.accel_tbl)
|
||||||
|
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
box.Add(lbl, 0, wx.ALL, 5)
|
||||||
|
box.Add(self.article_view, 0, wx.ALL, 5)
|
||||||
|
return box
|
||||||
|
|
||||||
|
def onSelect(self, event):
|
||||||
|
self.article_view.SelectAll()
|
||||||
|
|
||||||
|
def create_views_control(self):
|
||||||
|
lbl = wx.StaticText(self.panel, -1, _("Views"))
|
||||||
|
self.views = wx.TextCtrl(self.panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
box.Add(lbl, 0, wx.ALL, 5)
|
||||||
|
box.Add(self.views, 0, wx.ALL, 5)
|
||||||
|
return box
|
||||||
|
|
||||||
|
def create_tools_button(self):
|
||||||
|
self.tools = wx.Button(self.panel, -1, _("Actions"))
|
||||||
|
|
||||||
|
def create_dialog_buttons(self):
|
||||||
|
self.close = wx.Button(self.panel, wx.ID_CANCEL, _("Close"))
|
||||||
|
return self.close
|
||||||
|
|
||||||
|
def create_attachments(self):
|
||||||
|
lbl = wx.StaticText(self.panel, -1, _("Attachments"))
|
||||||
|
self.attachments = widgetUtils.list(self.panel, _("Type"), _("Title"), style=wx.LC_REPORT)
|
||||||
|
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
box.Add(lbl, 0, wx.ALL, 5)
|
||||||
|
box.Add(self.attachments.list, 0, wx.ALL, 5)
|
||||||
|
return box
|
||||||
|
|
||||||
|
def set_article(self, text):
|
||||||
|
if hasattr(self, "article_view"):
|
||||||
|
self.article_view.ChangeValue(text)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def insert_attachments(self, attachments):
|
||||||
|
for i in attachments:
|
||||||
|
self.attachments.insert_item(False, *i)
|
||||||
|
|
||||||
class displayFriendship(widgetUtils.BaseDialog):
|
class displayFriendship(widgetUtils.BaseDialog):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(displayFriendship, self).__init__(parent=None)
|
super(displayFriendship, self).__init__(parent=None)
|
||||||
|
Loading…
Reference in New Issue
Block a user