Fix AttributeError when saving data from the alternative tokens method
This commit is contained in:
commit
2b42c64160
@ -21,33 +21,6 @@ test_py3:
|
||||
- '%PYTHON3% -m coverage report --omit="test*"'
|
||||
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'
|
||||
|
||||
.alpha:
|
||||
type: deploy
|
||||
tags:
|
||||
- windows
|
||||
script:
|
||||
- pip install --upgrade pip
|
||||
- pip install --upgrade -r requirements.txt
|
||||
- copy changelog.md doc\changelog.md
|
||||
- cd doc
|
||||
- python documentation_importer.py
|
||||
- cd ..\src
|
||||
- python ..\doc\generator.py
|
||||
- python write_version_data.py
|
||||
- python setup.py py2exe
|
||||
- cd ..
|
||||
- cd scripts
|
||||
- python prepare_zipversion.py
|
||||
- cd ..
|
||||
- move src\socializer.zip socializer.zip
|
||||
only:
|
||||
- schedules
|
||||
artifacts:
|
||||
paths:
|
||||
- socializer.zip
|
||||
name: socializer
|
||||
expire_in: 1 day
|
||||
|
||||
alpha_python3:
|
||||
type: deploy
|
||||
tags:
|
||||
|
@ -7,6 +7,10 @@
|
||||
* When modifying volume of the playing audio, it will decrease or increase the volume by 2% instead of 5%.
|
||||
* For users with multiple soundcards, there is a new tab in the preferences dialogue of Socializer, called sound. From there, you can define what soundcard will be used for input and output.
|
||||
* it is possible to select the language used in socializer from the preferences dialog. When changing the language, the application must be restarted for the changes to take effect.
|
||||
* Improvements in conversation buffers:
|
||||
* it is possible to retrieve more items for conversation buffers. Due to API limitations, it is possible to load up to the last 600 messages for every conversation. Take into account that the process of loading more items takes some time. You will hear a message when the process is done.
|
||||
* It is possible to delete entire conversations from the buffer's tree, by using the menu key and selecting "delete conversation". The conversation will be removed from VK.
|
||||
* Read confirmations will be sent to VK as soon as you read the message. Before, read confirmations were being sent every 3 minutes to the social network.
|
||||
|
||||
## Changes in version 0.19 (13.03.2019)
|
||||
|
||||
|
@ -911,10 +911,11 @@ class chatBuffer(baseBuffer):
|
||||
|
||||
def insert(self, item, reversed=False):
|
||||
""" Add a new item to the list. Uses session.composefunc for parsing the dictionary and create a valid result for putting it in the list."""
|
||||
# as this tab is based in a text control, we have to overwrite the defaults.
|
||||
item_ = getattr(renderers, self.compose_function)(item, self.session)
|
||||
# the self.chat dictionary will have (first_line, last_line) as keys and message ID as a value for looking into it when needed.
|
||||
# Here we will get first and last line of a chat message appended to the history.
|
||||
values = self.tab.add_message(item_[0])
|
||||
values = self.tab.add_message(item_[0], reverse=reversed)
|
||||
self.chats[values] = item["id"]
|
||||
|
||||
def get_focused_post(self):
|
||||
@ -930,7 +931,6 @@ class chatBuffer(baseBuffer):
|
||||
# position[2]+1 is added because line may start with 0, while in wx.TextCtrl.GetNumberLines() that is not possible.
|
||||
if position[2]+1 >= i[0] and position[2]+1 < i[1]:
|
||||
id_ = self.chats[i]
|
||||
# print i
|
||||
break
|
||||
# Retrieve here the object based in id_
|
||||
if id_ != None:
|
||||
@ -946,9 +946,10 @@ class chatBuffer(baseBuffer):
|
||||
msg = self.get_focused_post()
|
||||
if msg == False: # Handle the case where the last line of the control cannot be matched to anything.
|
||||
return
|
||||
if "read_state" in msg and msg["read_state"] == 0 and msg["id"] not in self.reads and "out" in msg and msg["out"] == 0:
|
||||
# Mark unread conversations as read.
|
||||
if "read_state" in msg and msg["read_state"] == 0 and "out" in msg and msg["out"] == 0:
|
||||
self.session.soundplayer.play("message_unread.ogg")
|
||||
self.reads.append(msg["id"])
|
||||
call_threaded(self.session.vk.client.messages.markAsRead, peer_id=self.kwargs["peer_id"])
|
||||
self.session.db[self.name]["items"][-1]["read_state"] = 1
|
||||
if "attachments" in msg and len(msg["attachments"]) > 0:
|
||||
self.tab.attachments.list.Enable(True)
|
||||
@ -985,10 +986,11 @@ class chatBuffer(baseBuffer):
|
||||
event.Skip()
|
||||
|
||||
def get_items(self, show_nextpage=False):
|
||||
""" Update buffer with newest items or get older items in the buffer."""
|
||||
if self.can_get_items == False: return
|
||||
retrieved = True # Control variable for handling unauthorised/connection errors.
|
||||
retrieved = True
|
||||
try:
|
||||
num = getattr(self.session, "get_messages")(name=self.name, *self.args, **self.kwargs)
|
||||
num = getattr(self.session, "get_page")(show_nextpage=show_nextpage, name=self.name, *self.args, **self.kwargs)
|
||||
except VkApiError as err:
|
||||
log.error("Error {0}: {1}".format(err.code, err.error))
|
||||
retrieved = err.code
|
||||
@ -1001,20 +1003,38 @@ class chatBuffer(baseBuffer):
|
||||
self.create_tab(self.parent)
|
||||
# Add name to the new control so we could look for it when needed.
|
||||
self.tab.name = self.name
|
||||
|
||||
if show_nextpage == False:
|
||||
if self.tab.history.GetValue() != "" and num > 0:
|
||||
v = [i for i in self.session.db[self.name]["items"][:num]]
|
||||
# v.reverse()
|
||||
[self.insert(i, False) for i in v]
|
||||
else:
|
||||
[self.insert(i) for i in self.session.db[self.name]["items"][:num]]
|
||||
else:
|
||||
if num > 0:
|
||||
[self.insert(i, False) for i in self.session.db[self.name]["items"][:num]]
|
||||
# At this point we save more CPU and mathematical work if we just delete everything in the chat history and readd all messages.
|
||||
# Otherwise we'd have to insert new lines at the top and recalculate positions everywhere else.
|
||||
# Firstly, we'd have to save the current focused object so we will place the user in the right part of the text after loading everything again.
|
||||
focused_post = self.get_post()
|
||||
self.chats = dict()
|
||||
self.tab.history.SetValue("")
|
||||
v = [i for i in self.session.db[self.name]["items"]]
|
||||
[self.insert(i) for i in v]
|
||||
# Now it's time to set back the focus in the post.
|
||||
for i in self.chats.keys():
|
||||
if self.chats[i] == focused_post["id"]:
|
||||
line = i[0]
|
||||
self.tab.history.SetInsertionPoint(self.tab.history.XYToPosition(0, line))
|
||||
output.speak(_("Items loaded"))
|
||||
break
|
||||
if self.unread == True and num > 0:
|
||||
self.session.db[self.name]["items"][-1].update(read_state=0)
|
||||
return retrieved
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_("Getting more items..."))
|
||||
call_threaded(self.get_items, show_nextpage=True)
|
||||
|
||||
def add_attachment(self, *args, **kwargs):
|
||||
a = presenters.attachPresenter(session=self.session, view=views.attachDialog(voice_messages=True), interactor=interactors.attachInteractor())
|
||||
if len(a.attachments) != 0:
|
||||
@ -1093,7 +1113,6 @@ class chatBuffer(baseBuffer):
|
||||
def __init__(self, unread=False, *args, **kwargs):
|
||||
super(chatBuffer, self).__init__(*args, **kwargs)
|
||||
self.unread = unread
|
||||
self.reads = []
|
||||
self.chats = dict()
|
||||
self.peer_typing = 0
|
||||
self.last_keypress = time.time()
|
||||
|
@ -114,8 +114,6 @@ class Controller(object):
|
||||
self.window.realize()
|
||||
self.repeatedUpdate = RepeatingTimer(120, self.update_all_buffers)
|
||||
self.repeatedUpdate.start()
|
||||
self.readMarker = RepeatingTimer(60, self.mark_as_read)
|
||||
self.readMarker.start()
|
||||
|
||||
def complete_buffer_creation(self, buffer, name_, position):
|
||||
answer = buffer.get_items()
|
||||
@ -158,14 +156,6 @@ class Controller(object):
|
||||
call_threaded(self.chat_from_id, i["last_message"]["peer_id"], setfocus=False, unread=False)
|
||||
time.sleep(0.6)
|
||||
|
||||
def mark_as_read(self):
|
||||
for i in self.buffers:
|
||||
if hasattr(i, "reads") and len(i.reads) != 0:
|
||||
response = self.session.vk.client.messages.markAsRead(peer_id=i.kwargs["peer_id"])
|
||||
i.clear_reads()
|
||||
i.reads = []
|
||||
time.sleep(1)
|
||||
|
||||
def get_audio_albums(self, user_id=None, create_buffers=True, force_action=False):
|
||||
if self.session.settings["load_at_startup"]["audio_albums"] == False and force_action == False:
|
||||
return
|
||||
@ -350,13 +340,13 @@ class Controller(object):
|
||||
if "url" in audio_object and audio_object["url"] =="":
|
||||
self.notify(message=_("This file could not be played because it is not allowed in your country"))
|
||||
return
|
||||
call_threaded(player.player.play, audio_object, fresh=True)
|
||||
pub.sendMessage("play", object=audio_object, fresh=True)
|
||||
|
||||
def play_audios(self, audios):
|
||||
""" Play all audios passed in alist, putting the audio in a queue of the media player.
|
||||
@audios list: A list of Vk audio objects.
|
||||
"""
|
||||
player.player.play_all(audios, shuffle=self.window.player_shuffle.IsChecked())
|
||||
pub.sendMessage("play_all", list_of_songs=audios, shuffle=self.window.player_shuffle.IsChecked())
|
||||
|
||||
def view_post(self, post_object, controller_):
|
||||
""" Display the passed post in the passed post presenter.
|
||||
@ -392,7 +382,7 @@ class Controller(object):
|
||||
elif user_id > 2000000000:
|
||||
chat = self.session.vk.client.messages.getChat(chat_id=user_id-2000000000)
|
||||
name = chat["title"]
|
||||
wx.CallAfter(pub.sendMessage, "create_buffer", buffer_type="chatBuffer", buffer_title=name, parent_tab="chats", get_items=True, kwargs=dict(parent=self.window.tb, name="{0}_messages".format(user_id,), composefunc="render_message", session=self.session, unread=unread, count=200, peer_id=user_id, rev=0, extended=True, fields="id, user_id, date, read_state, out, body, attachments, deleted"))
|
||||
wx.CallAfter(pub.sendMessage, "create_buffer", buffer_type="chatBuffer", buffer_title=name, parent_tab="chats", get_items=True, kwargs=dict(parent=self.window.tb, name="{0}_messages".format(user_id,), composefunc="render_message", parent_endpoint="messages", endpoint="getHistory", session=self.session, unread=unread, count=200, peer_id=user_id, rev=0, extended=True, fields="id, user_id, date, read_state, out, body, attachments, deleted"))
|
||||
# if setfocus:
|
||||
# pos = self.window.search(buffer.name)
|
||||
# self.window.change_buffer(pos)
|
||||
@ -538,7 +528,7 @@ class Controller(object):
|
||||
data = [message]
|
||||
# Let's add this to the buffer.
|
||||
# ToDo: Clean this code and test how is the database working with this set to True.
|
||||
num = self.session.order_buffer(buffer.name, data, True)
|
||||
buffer.session.db[buffer.name]["items"].append(message)
|
||||
buffer.insert(self.session.db[buffer.name]["items"][-1], False)
|
||||
self.session.soundplayer.play("message_received.ogg")
|
||||
wx.CallAfter(self.reorder_buffer, buffer)
|
||||
@ -883,6 +873,7 @@ class Controller(object):
|
||||
else:
|
||||
option = menu.Append(wx.NewId(), _("Discard groups"))
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unload_community_buffers, menuitem=option)
|
||||
# Deal with video and audio albums' sections.
|
||||
elif current_buffer.name == "audio_albums":
|
||||
menu = wx.Menu()
|
||||
if self.session.settings["load_at_startup"]["audio_albums"] == False and not hasattr(self.session, "audio_albums"):
|
||||
@ -899,6 +890,9 @@ class Controller(object):
|
||||
else:
|
||||
option = menu.Append(wx.NewId(), _("Discard video albums"))
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unload_video_album_buffers, menuitem=option)
|
||||
elif current_buffer.name.endswith("_messages"):
|
||||
menu = menus.conversationBufferMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.delete_conversation, menuitem=menu.delete)
|
||||
|
||||
if menu != None:
|
||||
self.window.PopupMenu(menu, self.window.FindFocus().GetPosition())
|
||||
@ -1001,3 +995,12 @@ class Controller(object):
|
||||
self.window.remove_buffer(buff)
|
||||
self.buffers.remove(buffer)
|
||||
del self.session.video_albums
|
||||
|
||||
def delete_conversation(self, *args, **kwargs):
|
||||
current_buffer = self.get_current_buffer()
|
||||
d = commonMessages.delete_conversation()
|
||||
if d == widgetUtils.YES:
|
||||
results = self.session.vk.client.messages.deleteConversation(peer_id=current_buffer.kwargs["peer_id"])
|
||||
buff = self.window.search(current_buffer.name)
|
||||
self.window.remove_buffer(buff)
|
||||
self.buffers.remove(current_buffer)
|
@ -1,40 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Audio player module for socializer.
|
||||
As this player does not have (still) an associated GUI, I have decided to place here the code for the interactor, which connects a bunch of pubsub events, and the presenter itself.
|
||||
"""
|
||||
import sys
|
||||
import random
|
||||
import output
|
||||
import sound_lib
|
||||
import logging
|
||||
import sound_lib
|
||||
import output
|
||||
import config
|
||||
from sound_lib.config import BassConfig
|
||||
from sound_lib.stream import URLStream
|
||||
from sound_lib.main import BassError
|
||||
from mysc.repeating_timer import RepeatingTimer
|
||||
from pubsub import pub
|
||||
from mysc.repeating_timer import RepeatingTimer
|
||||
|
||||
player = None
|
||||
log = logging.getLogger("player")
|
||||
|
||||
# This function will be deprecated when the player works with pubsub events, as will no longer be needed to instantiate and import the player directly.
|
||||
def setup():
|
||||
global player
|
||||
if player == None:
|
||||
player = audioPlayer()
|
||||
|
||||
class audioPlayer(object):
|
||||
""" A media player which will play all passed URLS."""
|
||||
|
||||
def __init__(self):
|
||||
# control variable for checking if another file has been sent to the player before,
|
||||
# thus avoiding double file playback and other oddities happening in sound_lib from time to time.
|
||||
self.is_playing = False
|
||||
# This will be the URLStream handler
|
||||
self.stream = None
|
||||
self.vol = config.app["sound"]["volume"]
|
||||
# this variable is set to true when the URLPlayer is decoding something, thus it will block other calls to the play method.
|
||||
self.is_working = False
|
||||
# Playback queue.
|
||||
self.queue = []
|
||||
# Index of the currently playing track.
|
||||
self.playing_track = 0
|
||||
# Status of the player.
|
||||
self.stopped = True
|
||||
# Modify some default settings present in Bass so it will increase timeout connection, thus causing less "connection timed out" errors when playing.
|
||||
bassconfig = BassConfig()
|
||||
# Set timeout connection to 30 seconds.
|
||||
bassconfig["net_timeout"] = 30000
|
||||
pub.subscribe(self.play, "play")
|
||||
pub.subscribe(self.play_all, "play_all")
|
||||
pub.subscribe(self.pause, "pause")
|
||||
pub.subscribe(self.stop, "stop")
|
||||
pub.subscribe(self.play_next, "play_next")
|
||||
pub.subscribe(self.play_previous, "play_previous")
|
||||
|
||||
def play(self, url, set_info=True, fresh=False):
|
||||
def play(self, object, set_info=True, fresh=False):
|
||||
""" Play an URl Stream.
|
||||
@object dict: typically an audio object as returned by VK, with a "url" component which must be a valid URL to a media file.
|
||||
@set_info bool: If true, will set information about the currently playing audio in the application status bar.
|
||||
@fresh bool: If True, will remove everything playing in the queue and start this file only. otherwise it will play the new file but not remove the current queue."""
|
||||
if self.stream != None and self.stream.is_playing == True:
|
||||
try:
|
||||
self.stream.stop()
|
||||
@ -50,19 +72,15 @@ class audioPlayer(object):
|
||||
if self.is_working == False:
|
||||
self.is_working = True
|
||||
# Let's encode the URL as bytes if on Python 3
|
||||
if sys.version[0] == "3":
|
||||
url_ = bytes(url["url"], "utf-8")
|
||||
else:
|
||||
url_ = url["url"]
|
||||
url_ = bytes(object["url"], "utf-8")
|
||||
try:
|
||||
self.stream = URLStream(url=url_)
|
||||
except IndexError:
|
||||
log.error("Unable to play URL")
|
||||
log.error(url_)
|
||||
log.error("Unable to play URL %s" % (url_))
|
||||
return
|
||||
# Translators: {0} will be replaced with a song's title and {1} with the artist.
|
||||
if set_info:
|
||||
msg = _("Playing {0} by {1}").format(url["title"], url["artist"])
|
||||
msg = _("Playing {0} by {1}").format(object["title"], object["artist"])
|
||||
pub.sendMessage("update-status-bar", status=msg)
|
||||
self.stream.volume = self.vol/100.0
|
||||
self.stream.play()
|
||||
@ -70,6 +88,7 @@ class audioPlayer(object):
|
||||
self.is_working = False
|
||||
|
||||
def stop(self):
|
||||
""" Stop audio playback. """
|
||||
if self.stream != None and self.stream.is_playing == True:
|
||||
self.stream.stop()
|
||||
self.stopped = True
|
||||
@ -79,6 +98,7 @@ class audioPlayer(object):
|
||||
self.queue = []
|
||||
|
||||
def pause(self):
|
||||
""" pause the current playback, without destroying the queue or the current stream. If the stream is already paused this function will resume the playback. """
|
||||
if self.stream != None:
|
||||
if self.stream.is_playing == True:
|
||||
self.stream.pause()
|
||||
@ -105,11 +125,14 @@ class audioPlayer(object):
|
||||
if self.stream != None:
|
||||
self.stream.volume = self.vol/100.0
|
||||
|
||||
def play_all(self, list_of_urls, shuffle=False):
|
||||
def play_all(self, list_of_songs, shuffle=False):
|
||||
""" Play all passed songs and adds all of those to the queue.
|
||||
@list_of_songs list: A list of audio objects returned by VK.
|
||||
@shuffle bool: If True, the files will be played randomly."""
|
||||
self.playing_track = 0
|
||||
self.stop()
|
||||
# Skip all country restricted tracks as they are not playable here.
|
||||
self.queue = [i for i in list_of_urls if i["url"] != ""]
|
||||
self.queue = [i for i in list_of_songs if i["url"] != ""]
|
||||
if shuffle:
|
||||
random.shuffle(self.queue)
|
||||
self.play(self.queue[self.playing_track])
|
||||
@ -117,6 +140,7 @@ class audioPlayer(object):
|
||||
self.worker.start()
|
||||
|
||||
def player_function(self):
|
||||
""" Check if the stream has reached the end of the file so it will play the next song. """
|
||||
if self.stream != None and self.stream.is_playing == False and self.stopped == False and len(self.stream) == self.stream.position:
|
||||
if len(self.queue) == 0 or self.playing_track >= len(self.queue):
|
||||
self.worker.cancel()
|
||||
@ -126,6 +150,7 @@ class audioPlayer(object):
|
||||
self.play(self.queue[self.playing_track])
|
||||
|
||||
def play_next(self):
|
||||
""" Play the next song in the queue. """
|
||||
if len(self.queue) == 0:
|
||||
return
|
||||
if self.playing_track < len(self.queue)-1:
|
||||
@ -135,6 +160,7 @@ class audioPlayer(object):
|
||||
self.play(self.queue[self.playing_track])
|
||||
|
||||
def play_previous(self):
|
||||
""" Play the previous song in the queue. """
|
||||
if len(self.queue) == 0:
|
||||
return
|
||||
if self.playing_track <= 0:
|
||||
@ -144,6 +170,7 @@ class audioPlayer(object):
|
||||
self.play(self.queue[self.playing_track])
|
||||
|
||||
def check_is_playing(self):
|
||||
""" check if the player is already playing a stream. """
|
||||
if self.stream == None:
|
||||
return False
|
||||
if self.stream != None and self.stream.is_playing == False:
|
||||
|
@ -66,6 +66,9 @@ class vkSession(object):
|
||||
self.db[name] = {}
|
||||
self.db[name]["items"] = []
|
||||
first_addition = True
|
||||
# Handles chat messages case, as the buffer is inverted
|
||||
if name.endswith("_messages") and show_nextpage == True:
|
||||
show_nextpage = False
|
||||
for i in data:
|
||||
if "type" in i and not isinstance(i["type"], int) and (i["type"] == "wall_photo" or i["type"] == "photo_tag" or i["type"] == "photo"):
|
||||
log.debug("Skipping unsupported item... %r" % (i,))
|
||||
@ -124,8 +127,11 @@ class vkSession(object):
|
||||
config_filename = os.path.join(paths.config_path(), self.session_id, "vkconfig.json")
|
||||
self.vk.login(self.settings["vk"]["user"], self.settings["vk"]["password"], token=self.settings["vk"]["token"], secret=self.settings["vk"]["secret"], device_id=self.settings["vk"]["device_id"], alt_token=self.settings["vk"]["use_alternative_tokens"], filename=config_filename)
|
||||
self.settings["vk"]["token"] = self.vk.session_object.token["access_token"]
|
||||
try:
|
||||
self.settings["vk"]["secret"] = self.vk.session_object.secret
|
||||
self.settings["vk"]["device_id"] = self.vk.session_object.device_id
|
||||
except AttributeError:
|
||||
pass
|
||||
self.settings.write()
|
||||
self.logged = True
|
||||
self.get_my_data()
|
||||
@ -192,6 +198,10 @@ class vkSession(object):
|
||||
if data != None:
|
||||
if "count" not in kwargs:
|
||||
kwargs["count"] = 100
|
||||
# Let's handle a little exception when dealing with conversation buffers.
|
||||
# the first results of the query should be reversed before being sent to order_buffer.
|
||||
if type(data) == dict and "items" in data and endpoint == "getHistory" and kwargs["offset"] == 0:
|
||||
data["items"].reverse()
|
||||
if type(data) == dict:
|
||||
num = self.order_buffer(name, data["items"], show_nextpage)
|
||||
self.db[name]["offset"] = kwargs["offset"]+kwargs["count"]
|
||||
|
@ -64,3 +64,6 @@ def restart_program():
|
||||
|
||||
def community_no_items():
|
||||
return wx.MessageDialog(None, _("There are 0 items for this community."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def delete_conversation():
|
||||
return wx.MessageDialog(None, _("do you really want to delete all messages of this conversation in VK?"), _("Attention"), style=wx.ICON_QUESTION|wx.YES_NO).ShowModal()
|
||||
|
@ -125,3 +125,8 @@ class communityBufferMenu(wx.Menu):
|
||||
self.load_videos = load.Append(wx.NewId(), _("Load videos"))
|
||||
self.load_documents = load.Append(wx.NewId(), _("Load documents"))
|
||||
self.Append(wx.NewId(), _("Load"), load)
|
||||
|
||||
class conversationBufferMenu(wx.Menu):
|
||||
def __init__(self):
|
||||
super(conversationBufferMenu, self).__init__()
|
||||
self.delete = self.Append(wx.NewId(), _("Delete conversation"))
|
Loading…
Reference in New Issue
Block a user