diff options
author | Toshiaki Asai <toshi.alternative@gmail.com> | 2016-10-02 21:57:07 +0900 |
---|---|---|
committer | Toshiaki Asai <toshi.alternative@gmail.com> | 2016-10-02 21:57:07 +0900 |
commit | dfcf19a4b0f9195dda3cb4ab0dbe1ee30f4ecec0 (patch) | |
tree | 37be9be2f6aa3a2bc6a455ade2789b68653ade73 /core | |
parent | ba3fef7bd872d3b7c5767dd6232103ff197e1f22 (diff) | |
parent | 50ea08c85ec614ecba50fa398ff1777bb07d5d21 (diff) | |
download | mikutter-dfcf19a4b0f9195dda3cb4ab0dbe1ee30f4ecec0.tar.gz |
Merge branch 'hotfix/3.3' into hotfix/3.4
Diffstat (limited to 'core')
-rw-r--r-- | core/lib/mikutwitter/api_call_support.rb | 43 | ||||
-rw-r--r-- | core/lib/mikutwitter/api_shortcuts.rb | 29 | ||||
-rw-r--r-- | core/lib/mikutwitter/error.rb | 3 | ||||
-rw-r--r-- | core/mui/gtk_postbox.rb | 40 | ||||
-rw-r--r-- | core/plugin/streaming/streamer.rb | 10 |
5 files changed, 107 insertions, 18 deletions
diff --git a/core/lib/mikutwitter/api_call_support.rb b/core/lib/mikutwitter/api_call_support.rb index e2a132f3..99a0d72c 100644 --- a/core/lib/mikutwitter/api_call_support.rb +++ b/core/lib/mikutwitter/api_call_support.rb @@ -40,6 +40,7 @@ module MikuTwitter::ApiCallSupport # [multi] 名前(複数形) def self.defparser(uni, multi = :"#{uni}s", container = Array, defaults = {}) parser = lazy{ MikuTwitter::ApiCallSupport::Request::Parser.method(uni) } + defaults.freeze define_method(multi){ |options = {}| type_strict options => Hash json(defaults.merge(options)).next{ |node| @@ -76,7 +77,7 @@ module MikuTwitter::ApiCallSupport Thread.new{ JSON.parse(res.body).symbolize } } end defparser :user, :users, Users - defparser :message, :messages, Messages + defparser :message, :messages, Messages, tweet_mode: 'extended'.freeze defparser :list defparser :id defparser :direct_message @@ -92,7 +93,7 @@ module MikuTwitter::ApiCallSupport def search(options = {}) type_strict options => Hash - json(options).next{ |res| + json({tweet_mode: 'extended'.freeze}.merge(options)).next{ |res| Thread.new { Parser.messages res[:statuses] } } end def inspect @@ -103,9 +104,9 @@ module MikuTwitter::ApiCallSupport extend Parser def message(msg) - cnv = msg.convert_key(:text => :message, - :in_reply_to_user_id => :receiver, + cnv = msg.convert_key(:in_reply_to_user_id => :receiver, :in_reply_to_status_id => :replyto) + cnv[:message] = msg[:full_text] || msg[:text] cnv[:source] = $1 if cnv[:source].is_a?(String) and cnv[:source].match(/\A<a\s+.*>(.*?)<\/a>\Z/) cnv[:created] = (Time.parse(msg[:created_at]).localtime rescue Time.now) cnv[:user] = Message::MessageUser.new(user(msg[:user]), msg[:user]) @@ -117,6 +118,40 @@ module MikuTwitter::ApiCallSupport message(msg[:quoted_status]).add_quoted_by(message) end message end + # Streaming APIにはtweet_modeスイッチが効かないとかTwitterアホか!? + # ↓ + # Parser#message に、compat modeも受け付けるような改修を入れる + # ↓ + # Twitter「Streaming APIのcompatモードはちょっと中身が違うんじゃ」 + # see: https://dev.twitter.com/overview/api/upcoming-changes-to-tweets + # ↓ + # 死にたいのか!? + # ↓ + # 恒例の身売り話が出てくる + # see: http://www.afpbb.com/articles/-/3101961 + # ↓ + # 死ぬのか!? + def streaming_message(msg) + cnv = msg.convert_key(:in_reply_to_user_id => :receiver, + :in_reply_to_status_id => :replyto) + if msg[:extended_tweet] + cnv.delete(:extended_tweet) + cnv.merge!(msg[:extended_tweet]) + cnv[:message] = msg[:extended_tweet][:full_text] + else + cnv[:message] = msg[:text] + end + cnv[:source] = $1 if cnv[:source].is_a?(String) and cnv[:source].match(/\A<a\s+.*>(.*?)<\/a>\Z/) + cnv[:created] = (Time.parse(msg[:created_at]).localtime rescue Time.now) + cnv[:user] = Message::MessageUser.new(user(msg[:user]), msg[:user]) + cnv[:retweet] = streaming_message(msg[:retweeted_status]) if msg[:retweeted_status] + cnv[:exact] = [:created_at, :source, :user, :retweeted_status].all?{|k|msg.has_key?(k)} + message = cnv[:exact] ? Message.rewind(cnv) : Message.new_ifnecessary(cnv) + # search/tweets.json の戻り値のquoted_statusのuserがたまにnullだゾ〜 + if msg[:quoted_status].is_a?(Hash) and msg[:quoted_status][:user] + streaming_message(msg[:quoted_status]).add_quoted_by(message) end + message end + def messages(msgs) Messages.new msgs.map{ |msg| message(msg) } end diff --git a/core/lib/mikutwitter/api_shortcuts.rb b/core/lib/mikutwitter/api_shortcuts.rb index c4259c24..56ddd2ad 100644 --- a/core/lib/mikutwitter/api_shortcuts.rb +++ b/core/lib/mikutwitter/api_shortcuts.rb @@ -122,17 +122,38 @@ module MikuTwitter::APIShortcuts def update(message) text = message[:message] - replyto = message[:replyto] - receiver = message[:receiver] + replyto = message[:replyto] && Message.generate(message[:replyto]) + receiver = message[:receiver] && User.generate(message[:receiver]) iolist = message[:mediaiolist] + is_reply = !!(receiver || replyto) data = {:status => text } - data[:in_reply_to_user_id] = User.generate(receiver)[:id].to_s if receiver - data[:in_reply_to_status_id] = Message.generate(replyto)[:id].to_s if replyto + data[:in_reply_to_user_id] = receiver.id if receiver + data[:in_reply_to_status_id] = replyto.id if replyto + if is_reply + forecast_receivers = exclude_receivers = Set.new.freeze + if replyto + forecast_receivers += replyto.each_ancestor.map(&:user) + end + mentions = text.match(%r[\A((?:@[a-zA-Z0-9_]+\s+)+)]) + if mentions + specific_screen_names = mentions[1].split(/\s+/).map{|s|s[1, s.size]} + exclude_receivers += forecast_receivers.reject{|u| specific_screen_names.include?(u.idname) } + text = [*(specific_screen_names - forecast_receivers.map(&:idname)).map{|s|"@#{s}"}, text[mentions.end(0),text.size]].join(' '.freeze) + data[:status] = text + end + data[:auto_populate_reply_metadata] = true + data[:exclude_reply_user_ids] = exclude_receivers.map(&:id).join(',') unless exclude_receivers.empty? + end if iolist and !iolist.empty? Deferred.when(*iolist.collect{ |io| upload_media(io) }).next{|media_list| data[:media_ids] = media_list.map{|media| media['media_id'] }.join(",") (self/'statuses/update').message(data) } else + attachment_url = text.match(%r[\A(.+?)\s+(https?://twitter.com/(?:#!/)?(?:[a-zA-Z0-9_]+)/status(?:es)?/(?:\d+)(?:\?.*)?)\Z]m) + if attachment_url + data[:attachment_url] = attachment_url[2] + data[:status] = attachment_url[1] + end (self/'statuses/update').message(data) end end alias post update diff --git a/core/lib/mikutwitter/error.rb b/core/lib/mikutwitter/error.rb index d99850f3..c042bb09 100644 --- a/core/lib/mikutwitter/error.rb +++ b/core/lib/mikutwitter/error.rb @@ -35,6 +35,7 @@ def MikuTwitter.TwitterError(code=nil) MikuTwitter::CouldNotAuthenticateError = Class.new(MikuTwitter::TwitterError(32)) MikuTwitter::SpecifiedResourceWasNotFoundEerror = Class.new(MikuTwitter::TwitterError(34)) +MikuTwitter::AttachmentURLParameterIsInvalidEerror = Class.new(MikuTwitter::TwitterError(44)) MikuTwitter::SuspendOrNotPermittedError = Class.new(MikuTwitter::TwitterError(64)) MikuTwitter::APIVersionTooOldError = Class.new(MikuTwitter::TwitterError(68)) MikuTwitter::RateLimitError = Class.new(MikuTwitter::TwitterError(88)) @@ -55,3 +56,5 @@ MikuTwitter::DontHaveWriteAccessError = Class.new(MikuTwitter::TwitterError(261) MikuTwitter::CantMuteYourselfError = Class.new(MikuTwitter::TwitterError(271)) MikuTwitter::NotMutingError = Class.new(MikuTwitter::TwitterError(272)) MikuTwitter::DirectMessageExceedTheNumberOfCharacterError = Class.new(MikuTwitter::TwitterError(354)) +MikuTwitter::InReplyToStatusIdDoesNotExistError = Class.new(MikuTwitter::TwitterError(385)) +MikuTwitter::TooManyAttachmentResourceError = Class.new(MikuTwitter::TwitterError(386)) diff --git a/core/mui/gtk_postbox.rb b/core/mui/gtk_postbox.rb index 760067b6..5e1129c1 100644 --- a/core/mui/gtk_postbox.rb +++ b/core/mui/gtk_postbox.rb @@ -264,7 +264,7 @@ module Gtk # _related_widgets_ のうちどれもアクティブではなく、フォーカスが外れたら削除される設定の場合、このウィジェットを削除する def destroy_if_necessary(*related_widgets) - if(not(frozen?) and not([widget_post, *related_widgets].compact.any?{ |w| w.focus? }) and destructible?) + if(not(frozen? or destroyed?) and not([widget_post, *related_widgets].compact.any?{ |w| w.focus? }) and destructible?) destroy true end end @@ -289,16 +289,46 @@ module Gtk def remain_charcount if not widget_post.destroyed? - footer = if use_blind_footer? then UserConfig[:footer].size else 0 end - text = widget_post.buffer.text + text = trim_hidden_regions(widget_post.buffer.text + UserConfig[:footer]) Twitter::Extractor.extract_urls(text).map{|url| if url.length < posted_url_length(url) -(posted_url_length(url) - url.length) else url.length - posted_url_length(url) end - }.inject(140 - text.size - footer, &:+) + }.inject(140 - text.size, &:+) end end + def trim_hidden_regions(text) + trim_hidden_header(trim_hidden_footer(text)) + end + + # 文字列からhidden headerを除いた文字列を返す。 + # hidden headerが含まれていない場合は、 _text_ を返す。 + def trim_hidden_header(text) + mentions = text.match(%r[\A((?:@[a-zA-Z0-9_]+\s+)+)]) + forecast_receivers = Set.new.freeze + if reply? + forecast_receivers += @to.first.each_ancestor.map(&:user) + end + if mentions + specific_screen_names = mentions[1].split(/\s+/).map{|s|s[1, s.size]} + [*(specific_screen_names - forecast_receivers.map(&:idname)).map{|s|"@#{s}"}, text[mentions.end(0),text.size]].join(' '.freeze) + else + text + end + end + + # 文字列からhidden footerを除いた文字列を返す。 + # hidden footerが含まれていない場合は、 _text_ を返す。 + def trim_hidden_footer(text) + attachment_url = text.match(%r[\A(.*?)\s+(https?://twitter.com/(?:#!/)?(?:[a-zA-Z0-9_]+)/status(?:es)?/(?:\d+)(?:\?.*)?)\Z]m) + if attachment_url + attachment_url[1] + else + text + end + end + # URL _url_ がTwitterに投稿された時に何文字としてカウントされるかを返す # ==== Args # [url] String URL @@ -310,7 +340,7 @@ module Gtk def focus_out_event(widget, event=nil) options = @options Delayer.new{ - if(not(frozen?) and not(options.has_key?(:postboxstorage)) and post_is_empty?) + if(not(frozen? or destroyed?) and not(options.has_key?(:postboxstorage)) and post_is_empty?) destroy_if_necessary(widget_send, widget_tool, *@reply_widgets) end } false end diff --git a/core/plugin/streaming/streamer.rb b/core/plugin/streaming/streamer.rb index 9a3b1e5e..bd7a7caf 100644 --- a/core/plugin/streaming/streamer.rb +++ b/core/plugin/streaming/streamer.rb @@ -110,7 +110,7 @@ module ::Plugin::Streaming defevent(:update, true) do |data| events = {update: Messages.new, mention: Messages.new, mypost: Messages.new} data.each { |json| - msg = MikuTwitter::ApiCallSupport::Request::Parser.message(json.symbolize) + msg = MikuTwitter::ApiCallSupport::Request::Parser.streaming_message(json.symbolize) events[:update] << msg events[:mention] << msg if msg.to_me? events[:mypost] << msg if msg.from_me? } @@ -122,13 +122,13 @@ module ::Plugin::Streaming defevent(:favorite) do |json| by = MikuTwitter::ApiCallSupport::Request::Parser.user(json['source'].symbolize) - to = MikuTwitter::ApiCallSupport::Request::Parser.message(json['target_object'].symbolize) + to = MikuTwitter::ApiCallSupport::Request::Parser.streaming_message(json['target_object'].symbolize) if(to.respond_to?(:add_favorited_by)) to.add_favorited_by(by, Time.parse(json['created_at'])) end end defevent(:unfavorite) do |json| by = MikuTwitter::ApiCallSupport::Request::Parser.user(json['source'].symbolize) - to = MikuTwitter::ApiCallSupport::Request::Parser.message(json['target_object'].symbolize) + to = MikuTwitter::ApiCallSupport::Request::Parser.streaming_message(json['target_object'].symbolize) if(to.respond_to?(:remove_favorited_by)) to.remove_favorited_by(by) end end @@ -143,12 +143,12 @@ module ::Plugin::Streaming defevent(:retweeted_retweet) do |json| by = MikuTwitter::ApiCallSupport::Request::Parser.user(json['source'].symbolize) #to = MikuTwitter::ApiCallSupport::Request::Parser.user(json['target'].symbolize) - target_object = MikuTwitter::ApiCallSupport::Request::Parser.message(json['target_object'].symbolize) + target_object = MikuTwitter::ApiCallSupport::Request::Parser.streaming_message(json['target_object'].symbolize) source_object = target_object.retweet_source source_object.add_retweet_user(by, Time.parse(json['created_at'])) end defevent(:quoted_tweet) do |json| - MikuTwitter::ApiCallSupport::Request::Parser.message(json['target_object'].symbolize) end + MikuTwitter::ApiCallSupport::Request::Parser.streaming_message(json['target_object'].symbolize) end defevent(:follow) do |json| source = MikuTwitter::ApiCallSupport::Request::Parser.user(json['source'].symbolize) |