From 77d13fdc28334d4e660af4e63bbb037542cc55b6 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Wed, 10 Nov 2021 15:14:40 -0600 Subject: [PATCH] Added support for posting polls --- src/controller/buffers/twitter/base.py | 3 +- src/controller/messages.py | 12 +++- src/sessions/twitter/session.py | 33 ++++++++-- src/wxUI/dialogs/twitterDialogs/__init__.py | 2 +- .../dialogs/twitterDialogs/tweetDialogs.py | 65 +++++++++++++++++++ 5 files changed, 106 insertions(+), 9 deletions(-) diff --git a/src/controller/buffers/twitter/base.py b/src/controller/buffers/twitter/base.py index ae22a576..34196b7b 100644 --- a/src/controller/buffers/twitter/base.py +++ b/src/controller/buffers/twitter/base.py @@ -457,11 +457,10 @@ class BaseBuffer(base.Buffer): if hasattr(tweet, "retweeted_status"): tweet = tweet.retweeted_status retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), max=256, thread_mode=False) - retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), max=256, thread_mode=False) if retweet.message.ShowModal() == widgetUtils.OK: text = retweet.message.text.GetValue() text = text+" https://twitter.com/{0}/status/{1}".format(self.session.get_user(tweet.user).screen_name, id) - tweet_data = dict(text=text, attachments=retweet.attachments) + tweet_data = dict(text=text, attachments=retweet.attachments, poll_period=retweet.poll_period, poll_options=retweet.poll_options) call_threaded(self.session.send_tweet, *[tweet_data]) if hasattr(retweet.message, "destroy"): retweet.message.destroy() diff --git a/src/controller/messages.py b/src/controller/messages.py index 67c8ec40..3fb52d38 100644 --- a/src/controller/messages.py +++ b/src/controller/messages.py @@ -131,7 +131,11 @@ class basicTweet(object): self.text_processor() def on_attach_poll(self, *args, **kwargs): - pass + dlg = twitterDialogs.poll() + if dlg.ShowModal() == wx.ID_OK: + self.poll_options = dlg.get_options() + self.poll_period = 60*24*dlg.period.GetValue() + dlg.Destroy() def remove_attachment(self, *args, **kwargs): attachment = self.message.attachments.GetFocusedItem() @@ -144,6 +148,8 @@ class basicTweet(object): class tweet(basicTweet): def __init__(self, session, title, caption, text="", max=280, messageType="tweet", *args, **kwargs): self.thread = [] + self.poll_options = None + self.poll_period = None super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs) widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) if hasattr(self.message, "add_tweet"): @@ -159,9 +165,11 @@ class tweet(basicTweet): def add_tweet(self, event, update_gui=True, *args, **kwargs): text = self.message.text.GetValue() attachments = self.attachments[::] - tweetdata = dict(text=text, attachments=attachments) + tweetdata = dict(text=text, attachments=attachments, poll_options=self.poll_options, poll_period=self.poll_period) self.thread.append(tweetdata) self.attachments = [] + self.poll_options = None + self.poll_period = None if update_gui: self.message.reset_controls() self.message.add_item(item=[text, len(attachments)], list_type="tweet") diff --git a/src/sessions/twitter/session.py b/src/sessions/twitter/session.py index 4e732b22..40f40df5 100644 --- a/src/sessions/twitter/session.py +++ b/src/sessions/twitter/session.py @@ -218,6 +218,30 @@ class Session(base.baseSession): if _sound != None: self.sound.play(_sound) return val + def api_call_v2(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs): + finished = False + tries = 0 + if preexec_message: + output.speak(preexec_message, True) + while finished==False and tries < 25: + try: + val = getattr(self.twitter_v2, call_name)(*args, **kwargs) + finished = True + except TweepyException as e: + log.exception("Error sending the tweet.") + output.speak(str(e)) + val = None + if type(e) != NotFound and type(e) != Forbidden: + tries = tries+1 + time.sleep(5) + elif report_failure: + output.speak(_("%s failed. Reason: %s") % (action, str(e))) + finished = True + if report_success: + output.speak(_("%s succeeded.") % action) + if _sound != None: self.sound.play(_sound) + return val + def search(self, name, *args, **kwargs): """ Search in twitter, passing args and kwargs as arguments to the Twython function.""" tl = self.twitter.search_tweets(*args, **kwargs) @@ -597,8 +621,9 @@ class Session(base.baseSession): in_reply_to_status_id = None for obj in tweets: if len(obj["attachments"]) == 0: - item = self.api_call(call_name="update_status", status=obj["text"], _sound="tweet_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id) - in_reply_to_status_id = item.id + item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"]) + print(item) + in_reply_to_status_id = item.data.id else: media_ids = [] for i in obj["attachments"]: @@ -606,8 +631,8 @@ class Session(base.baseSession): if i["type"] == "photo": self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"]) media_ids.append(img.media_id) - item = self.api_call(call_name="update_status", status=obj["text"], _sound="tweet_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id, media_ids=media_ids) - in_reply_to_status_id = item.id + item = self.api_call_v2(call_name="create_tweet", status=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"]) + in_reply_to_status_id = item.data.id def reply(self, text="", in_reply_to_status_id=None, attachments=[], *args, **kwargs): if len(attachments) == 0: diff --git a/src/wxUI/dialogs/twitterDialogs/__init__.py b/src/wxUI/dialogs/twitterDialogs/__init__.py index c6b568e0..6a829f06 100644 --- a/src/wxUI/dialogs/twitterDialogs/__init__.py +++ b/src/wxUI/dialogs/twitterDialogs/__init__.py @@ -1 +1 @@ -from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet \ No newline at end of file +from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll \ No newline at end of file diff --git a/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py b/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py index d175b033..d5fd35a6 100644 --- a/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py +++ b/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py @@ -469,3 +469,68 @@ class viewNonTweet(wx.Dialog): def enable_button(self, buttonName): if hasattr(self, buttonName): return getattr(self, buttonName).Enable() + +class poll(wx.Dialog): + def __init__(self, *args, **kwds): + super(poll, self).__init__(parent=None, id=wx.NewId(), title=_("Add a poll")) + sizer_1 = wx.BoxSizer(wx.VERTICAL) + period_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_1.Add(period_sizer, 1, wx.EXPAND, 0) + label_period = wx.StaticText(self, wx.ID_ANY, _("Participation time (in days)")) + period_sizer.Add(label_period, 0, 0, 0) + self.period = wx.SpinCtrl(self, wx.ID_ANY) + self.period.SetFocus() + self.period.SetRange(1, 7) + self.period.SetValue(7) + period_sizer.Add(self.period, 0, 0, 0) + sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Choices")), wx.VERTICAL) + sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) + option1_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option1_sizer, 1, wx.EXPAND, 0) + label_2 = wx.StaticText(self, wx.ID_ANY, _("Option 1")) + option1_sizer.Add(label_2, 0, 0, 0) + self.option1 = wx.TextCtrl(self, wx.ID_ANY, "") + option1_sizer.Add(self.option1, 0, 0, 0) + option2_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option2_sizer, 1, wx.EXPAND, 0) + label_3 = wx.StaticText(self, wx.ID_ANY, _("Option 2")) + option2_sizer.Add(label_3, 0, 0, 0) + self.option2 = wx.TextCtrl(self, wx.ID_ANY, "") + option2_sizer.Add(self.option2, 0, 0, 0) + option3_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option3_sizer, 1, wx.EXPAND, 0) + label_4 = wx.StaticText(self, wx.ID_ANY, _("Option 3")) + option3_sizer.Add(label_4, 0, 0, 0) + self.option3 = wx.TextCtrl(self, wx.ID_ANY, "") + option3_sizer.Add(self.option3, 0, 0, 0) + option4_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option4_sizer, 1, wx.EXPAND, 0) + label_5 = wx.StaticText(self, wx.ID_ANY, _("Option 4")) + option4_sizer.Add(label_5, 0, 0, 0) + self.option4 = wx.TextCtrl(self, wx.ID_ANY, "") + option4_sizer.Add(self.option4, 0, 0, 0) + btn_sizer = wx.StdDialogButtonSizer() + sizer_1.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.button_OK = wx.Button(self, wx.ID_OK) + self.button_OK.SetDefault() + self.button_OK.Bind(wx.EVT_BUTTON, self.validate_data) + btn_sizer.AddButton(self.button_OK) + self.button_CANCEL = wx.Button(self, wx.ID_CANCEL, "") + btn_sizer.AddButton(self.button_CANCEL) + btn_sizer.Realize() + self.SetSizer(sizer_1) + sizer_1.Fit(self) + self.SetAffirmativeId(self.button_OK.GetId()) + self.SetEscapeId(self.button_CANCEL.GetId()) + self.Layout() + + def get_options(self): + controls = [self.option1, self.option2, self.option3, self.option4] + options = [option.GetValue() for option in controls if option.GetValue() != ""] + return options + + def validate_data(self, *args, **kwargs): + options = self.get_options() + if len(options) < 2: + return wx.MessageDialog(self, _("Please make sure you have provided at least two options for the poll."), _("Not enough information"), wx.ICON_ERROR).ShowModal() + self.EndModal(wx.ID_OK) \ No newline at end of file