Refactored twitter channel backend.
This commit is contained in:
parent
d80ddecc44
commit
dc7d821728
6 changed files with 451 additions and 429 deletions
|
@ -30,32 +30,40 @@ Example:
|
||||||
{
|
{
|
||||||
"id":1,
|
"id":1,
|
||||||
"area":"Twitter::Inbound",
|
"area":"Twitter::Inbound",
|
||||||
"adapter":"Twitter2",
|
"adapter":"Twitter",
|
||||||
"group_id:": 1,
|
"group_id:": 1,
|
||||||
"options":{
|
"options":{
|
||||||
|
auth: {
|
||||||
"consumer_key":"PJ4c3dYYRtSZZZdOKo8ow",
|
"consumer_key":"PJ4c3dYYRtSZZZdOKo8ow",
|
||||||
"consumer_secret":"ggAdnJE2Al1Vv0cwwvX5bdvKOieFs0vjCIh5M8Dxk",
|
"consumer_secret":"ggAdnJE2Al1Vv0cwwvX5bdvKOieFs0vjCIh5M8Dxk",
|
||||||
"oauth_token":"293437546-xxRa9g74CercnU5AvY1uQwLLGIYrV1ezYtpX8oKW",
|
"oauth_token":"293437546-xxRa9g74CercnU5AvY1uQwLLGIYrV1ezYtpX8oKW",
|
||||||
"oauth_token_secret":"ju0E4l9OdY2Lh1iTKMymAu6XVfOaU2oGxmcbIMRZQK4",
|
"oauth_token_secret":"ju0E4l9OdY2Lh1iTKMymAu6XVfOaU2oGxmcbIMRZQK4",
|
||||||
|
},
|
||||||
|
"sync":{
|
||||||
"search":[
|
"search":[
|
||||||
{
|
{
|
||||||
"item":"#otrs",
|
"item":"#otrs",
|
||||||
"group_id":1,
|
"type": "mixed", # optional, possible 'mixed' (default), 'recent', 'popular'
|
||||||
|
"group": "OTRS",
|
||||||
|
"limit": 1, # optional
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"item":"#zombie42",
|
"item":"#zombie23",
|
||||||
"group_id":1,
|
"group": "Zombie Apocalypse Early Warning System",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"item":"#otterhub",
|
"item":"#otterhub",
|
||||||
"group_id":1,
|
"group": "Community",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mentions" {
|
"mentions" {
|
||||||
"group_id":1,
|
"group": "Twitter",
|
||||||
|
"limit": 100, # optional
|
||||||
},
|
},
|
||||||
"direct_messages": {
|
"direct_messages": {
|
||||||
"group_id":1,
|
"group": "Twitter",
|
||||||
|
"limit": 1, # optional
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active":true,
|
"active":true,
|
||||||
|
|
100
app/models/channel/twitter.rb
Normal file
100
app/models/channel/twitter.rb
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
# Copyright (C) 2012-2015 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Channel::Twitter
|
||||||
|
|
||||||
|
def fetch (channel)
|
||||||
|
|
||||||
|
@channel = channel
|
||||||
|
@tweet = Tweet.new( @channel[:options][:auth] )
|
||||||
|
@sync = @channel[:options][:sync]
|
||||||
|
|
||||||
|
Rails.logger.debug "twitter fetch started"
|
||||||
|
|
||||||
|
fetch_search
|
||||||
|
fetch_mentions
|
||||||
|
fetch_direct_messages
|
||||||
|
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
Rails.logger.debug 'twitter fetch completed'
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(article, _notification = false)
|
||||||
|
|
||||||
|
@channel = Channel.find_by( area: 'Twitter::Inbound', active: true )
|
||||||
|
@tweet = Tweet.new( @channel[:options][:auth] )
|
||||||
|
@sync = @channel[:options][:sync]
|
||||||
|
|
||||||
|
tweet = @tweet.from_article(article)
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
tweet
|
||||||
|
end
|
||||||
|
|
||||||
|
def disconnect
|
||||||
|
@tweet.disconnect
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_search
|
||||||
|
|
||||||
|
return if !@sync[:search]
|
||||||
|
return if @sync[:search].length == 0
|
||||||
|
|
||||||
|
# search results
|
||||||
|
@sync[:search].each { |search|
|
||||||
|
|
||||||
|
result_type = search[:type] || 'mixed'
|
||||||
|
|
||||||
|
Rails.logger.debug " - searching for '#{search[:term]}'"
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
@tweet.client.search( search[:term], result_type: result_type ).collect{ |tweet|
|
||||||
|
|
||||||
|
break if search[:limit] && search[:limit] <= counter
|
||||||
|
break if Ticket::Article.find_by( message_id: tweet.id.to_s )
|
||||||
|
|
||||||
|
@tweet.to_group( tweet, search[:group] )
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_mentions
|
||||||
|
|
||||||
|
return if !@sync[:mentions]
|
||||||
|
|
||||||
|
Rails.logger.debug ' - searching for mentions'
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
@tweet.client.mentions_timeline.each { |tweet|
|
||||||
|
|
||||||
|
break if @sync[:mentions][:limit] && @sync[:mentions][:limit] <= counter
|
||||||
|
break if Ticket::Article.find_by( message_id: tweet.id.to_s )
|
||||||
|
|
||||||
|
@tweet.to_group( tweet, @sync[:mentions][:group] )
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_direct_messages
|
||||||
|
|
||||||
|
return if !@sync[:direct_messages]
|
||||||
|
|
||||||
|
Rails.logger.debug ' - searching for direct_messages'
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
@tweet.client.direct_messages.each { |tweet|
|
||||||
|
|
||||||
|
break if @sync[:direct_messages][:limit] && @sync[:direct_messages][:limit] <= counter
|
||||||
|
break if Ticket::Article.find_by( message_id: tweet.id.to_s )
|
||||||
|
|
||||||
|
@tweet.to_group( tweet, @sync[:direct_messages][:group] )
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,303 +0,0 @@
|
||||||
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
require 'twitter'
|
|
||||||
|
|
||||||
class Channel::TWITTER2
|
|
||||||
def connect(channel)
|
|
||||||
@client = Twitter::REST::Client.new do |config|
|
|
||||||
config.consumer_key = channel[:options][:consumer_key]
|
|
||||||
config.consumer_secret = channel[:options][:consumer_secret]
|
|
||||||
config.access_token = channel[:options][:oauth_token]
|
|
||||||
config.access_token_secret = channel[:options][:oauth_token_secret]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def disconnect
|
|
||||||
|
|
||||||
return if !@client
|
|
||||||
|
|
||||||
@client = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch (channel)
|
|
||||||
|
|
||||||
Rails.logger.info "fetching tweets (oauth_token#{channel[:options][:oauth_token]})"
|
|
||||||
@client = connect(channel)
|
|
||||||
|
|
||||||
# search results
|
|
||||||
if channel[:options][:search]
|
|
||||||
channel[:options][:search].each { |search|
|
|
||||||
Rails.logger.info " - searching for #{search[:item]}"
|
|
||||||
tweets = []
|
|
||||||
@client.search( search[:item], count: 50, result_type: 'recent' ).collect do |tweet|
|
|
||||||
tweets.push tweet
|
|
||||||
end
|
|
||||||
@article_type = 'twitter status'
|
|
||||||
fetch_loop( tweets, channel, search[:group] )
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# mentions
|
|
||||||
if channel[:options][:mentions]
|
|
||||||
Rails.logger.info ' - searching for mentions'
|
|
||||||
tweets = @client.mentions_timeline
|
|
||||||
@article_type = 'twitter status'
|
|
||||||
fetch_loop( tweets, channel, channel[:options][:mentions][:group] )
|
|
||||||
end
|
|
||||||
|
|
||||||
# direct messages
|
|
||||||
if channel[:options][:direct_messages]
|
|
||||||
Rails.logger.info ' - searching for direct_messages'
|
|
||||||
tweets = @client.direct_messages
|
|
||||||
@article_type = 'twitter direct-message'
|
|
||||||
fetch_loop( tweets, channel, channel[:options][:direct_messages][:group] )
|
|
||||||
end
|
|
||||||
Rails.logger.info 'done'
|
|
||||||
disconnect
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_loop( tweets, channel, group )
|
|
||||||
|
|
||||||
# get all tweets
|
|
||||||
all_tweets = []
|
|
||||||
result_class = tweets.class
|
|
||||||
if result_class.to_s == 'Array'
|
|
||||||
all_tweets = tweets
|
|
||||||
elsif result_class.to_s == 'Twitter::SearchResults'
|
|
||||||
tweets.results.map do |tweet|
|
|
||||||
all_tweets.push tweet
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Rails.logger.error 'UNKNOWN: ' + result_class.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# find tweets
|
|
||||||
all_tweets.each do |tweet|
|
|
||||||
|
|
||||||
# check if tweet is already imported
|
|
||||||
article = Ticket::Article.find_by( message_id: tweet.id.to_s )
|
|
||||||
|
|
||||||
# check if sender already exists
|
|
||||||
next if article
|
|
||||||
|
|
||||||
# use transaction
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
|
|
||||||
# reset current_user
|
|
||||||
UserInfo.current_user_id = 1
|
|
||||||
|
|
||||||
Rails.logger.info 'import tweet'
|
|
||||||
fetch_import( tweet, channel, group )
|
|
||||||
end
|
|
||||||
|
|
||||||
# execute ticket events
|
|
||||||
Observer::Ticket::Notification.transaction
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_import(tweet, channel, group)
|
|
||||||
|
|
||||||
# do sender lockup if needed
|
|
||||||
sender = nil
|
|
||||||
|
|
||||||
# status (full user data is included)
|
|
||||||
if tweet.respond_to?('user')
|
|
||||||
sender = tweet.user
|
|
||||||
|
|
||||||
# direct message (full user data is included)
|
|
||||||
elsif tweet.respond_to?('sender')
|
|
||||||
sender = tweet.sender
|
|
||||||
|
|
||||||
# search (no user data is included, do extra lookup)
|
|
||||||
elsif tweet.respond_to?('from_user_id')
|
|
||||||
begin
|
|
||||||
sender = @client.user(tweet.from_user_id)
|
|
||||||
rescue => e
|
|
||||||
Rails.logger.error 'Exception: twitter: ' + e.inspect
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if parent exists
|
|
||||||
user = nil, ticket = nil, article = nil
|
|
||||||
if tweet.respond_to?('in_reply_to_status_id') && tweet.in_reply_to_status_id && tweet.in_reply_to_status_id.to_s != ''
|
|
||||||
Rails.logger.info 'import in_reply_tweet ' + tweet.in_reply_to_status_id.to_s
|
|
||||||
tweet_sub = @client.status( tweet.in_reply_to_status_id )
|
|
||||||
#Rails.logger.debug tweet_sub.inspect
|
|
||||||
(user, ticket, article) = fetch_import(tweet_sub, channel, group)
|
|
||||||
end
|
|
||||||
|
|
||||||
# create stuff
|
|
||||||
user = fetch_user_create(tweet, sender)
|
|
||||||
if !ticket
|
|
||||||
Rails.logger.info 'create new ticket...'
|
|
||||||
ticket = fetch_ticket_create(user, tweet, sender, channel, group)
|
|
||||||
end
|
|
||||||
article = fetch_article_create(user, ticket, tweet, sender)
|
|
||||||
[user, ticket, article]
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_user_create(_tweet, sender)
|
|
||||||
# create sender in db
|
|
||||||
# puts tweet.inspect
|
|
||||||
# user = User.where( :login => tweet.sender.screen_name ).first
|
|
||||||
auth = Authorization.find_by( uid: sender.id, provider: 'twitter' )
|
|
||||||
user = nil
|
|
||||||
if auth
|
|
||||||
Rails.logger.info 'user_id', auth.user_id
|
|
||||||
user = User.find_by( id: auth.user_id )
|
|
||||||
end
|
|
||||||
if !user
|
|
||||||
Rails.logger.info 'create user...'
|
|
||||||
roles = Role.where( name: 'Customer' )
|
|
||||||
user = User.create(
|
|
||||||
login: sender.screen_name,
|
|
||||||
firstname: sender.name,
|
|
||||||
lastname: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
image_source: sender.profile_image_url.to_s,
|
|
||||||
note: sender.description,
|
|
||||||
active: true,
|
|
||||||
roles: roles,
|
|
||||||
updated_by_id: 1,
|
|
||||||
created_by_id: 1
|
|
||||||
)
|
|
||||||
Rails.logger.info 'autentication create...'
|
|
||||||
authentication = Authorization.create(
|
|
||||||
uid: sender.id,
|
|
||||||
username: sender.screen_name,
|
|
||||||
user_id: user.id,
|
|
||||||
provider: 'twitter'
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Rails.logger.info 'user exists'
|
|
||||||
end
|
|
||||||
|
|
||||||
# set current user
|
|
||||||
UserInfo.current_user_id = user.id
|
|
||||||
|
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_ticket_create(user, tweet, _sender, _channel, group)
|
|
||||||
|
|
||||||
#Rails.logger.info '+++++++++++++++++++++++++++' + tweet.inspect
|
|
||||||
# check if ticket exists
|
|
||||||
if tweet.respond_to?('in_reply_to_status_id') && tweet.in_reply_to_status_id && tweet.in_reply_to_status_id.to_s != ''
|
|
||||||
Rails.logger.info 'tweet.in_reply_to_status_id found: ' + tweet.in_reply_to_status_id.to_s
|
|
||||||
article = Ticket::Article.find_by( message_id: tweet.in_reply_to_status_id.to_s )
|
|
||||||
if article
|
|
||||||
Rails.logger.info 'article with id found tweet.in_reply_to_status_id found: ' + tweet.in_reply_to_status_id.to_s
|
|
||||||
return article.ticket
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# find if record already exists
|
|
||||||
article = Ticket::Article.find_by( message_id: tweet.id.to_s )
|
|
||||||
if article
|
|
||||||
return article.ticket
|
|
||||||
end
|
|
||||||
|
|
||||||
ticket = nil
|
|
||||||
if @article_type == 'twitter direct-message'
|
|
||||||
ticket = Ticket.find_by( customer_id: user.id )
|
|
||||||
if ticket
|
|
||||||
state_type = Ticket::StateType.where( ticket.state.state_type_id )
|
|
||||||
if state_type.name == 'closed' || state_type.name == 'closed'
|
|
||||||
ticket = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if !ticket
|
|
||||||
group = Group.find_by( name: group )
|
|
||||||
group_id = 1
|
|
||||||
if group
|
|
||||||
group_id = group.id
|
|
||||||
end
|
|
||||||
state = Ticket::State.find_by( name: 'new' )
|
|
||||||
state_id = 1
|
|
||||||
if state
|
|
||||||
state_id = state.id
|
|
||||||
end
|
|
||||||
priority = Ticket::Priority.find_by( name: '2 normal' )
|
|
||||||
priority_id = 1
|
|
||||||
if priority
|
|
||||||
priority_id = priority.id
|
|
||||||
end
|
|
||||||
ticket = Ticket.create(
|
|
||||||
group_id: group_id,
|
|
||||||
customer_id: user.id,
|
|
||||||
title: tweet.text[0, 40],
|
|
||||||
state_id: state_id,
|
|
||||||
priority_id: priority_id,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
ticket
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_article_create( _user, ticket, tweet, sender )
|
|
||||||
|
|
||||||
# find if record already exists
|
|
||||||
article = Ticket::Article.find_by( message_id: tweet.id.to_s )
|
|
||||||
return article if article
|
|
||||||
|
|
||||||
# set ticket state to open if not new
|
|
||||||
if ticket.state.name != 'new'
|
|
||||||
ticket.state = Ticket::State.find_by( name: 'open' )
|
|
||||||
ticket.save
|
|
||||||
end
|
|
||||||
|
|
||||||
# import tweet
|
|
||||||
to = nil
|
|
||||||
if tweet.respond_to?('recipient')
|
|
||||||
to = tweet.recipient.name
|
|
||||||
end
|
|
||||||
|
|
||||||
article = Ticket::Article.create(
|
|
||||||
ticket_id: ticket.id,
|
|
||||||
type_id: Ticket::Article::Type.find_by( name: @article_type ).id,
|
|
||||||
sender_id: Ticket::Article::Sender.find_by( name: 'Customer' ).id,
|
|
||||||
body: tweet.text,
|
|
||||||
from: sender.name,
|
|
||||||
to: to,
|
|
||||||
message_id: tweet.id,
|
|
||||||
internal: false,
|
|
||||||
)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def send(attr, _notification = false)
|
|
||||||
# Rails.logger.debug('tweeeeettttt!!!!!!')
|
|
||||||
channel = Channel.find_by( area: 'Twitter::Inbound', active: true )
|
|
||||||
|
|
||||||
client = Twitter::REST::Client.new do |config|
|
|
||||||
config.consumer_key = channel[:options][:consumer_key]
|
|
||||||
config.consumer_secret = channel[:options][:consumer_secret]
|
|
||||||
config.access_token = channel[:options][:oauth_token]
|
|
||||||
config.access_token_secret = channel[:options][:oauth_token_secret]
|
|
||||||
end
|
|
||||||
if attr[:type] == 'twitter direct-message'
|
|
||||||
Rails.logger.info 'to:' + attr[:to].to_s
|
|
||||||
dm = client.create_direct_message(
|
|
||||||
attr[:to].to_s,
|
|
||||||
attr[:body].to_s,
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
# Rails.logger.info dm.inspect
|
|
||||||
return dm
|
|
||||||
end
|
|
||||||
|
|
||||||
return if attr[:type] != 'twitter status'
|
|
||||||
|
|
||||||
message = client.update(
|
|
||||||
attr[:body].to_s,
|
|
||||||
{
|
|
||||||
in_reply_to_status_id: attr[:in_reply_to]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# Rails.logger.debug message.inspect
|
|
||||||
message
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,5 +1,8 @@
|
||||||
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
|
||||||
|
require 'channel/twitter'
|
||||||
|
|
||||||
class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
||||||
observe 'ticket::_article'
|
observe 'ticket::_article'
|
||||||
|
|
||||||
|
@ -10,24 +13,21 @@ class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
||||||
|
|
||||||
# if sender is customer, do not communication
|
# if sender is customer, do not communication
|
||||||
sender = Ticket::Article::Sender.lookup( id: record.sender_id )
|
sender = Ticket::Article::Sender.lookup( id: record.sender_id )
|
||||||
return 1 if sender.nil?
|
return if sender.nil?
|
||||||
return 1 if sender['name'] == 'Customer'
|
return if sender['name'] == 'Customer'
|
||||||
|
|
||||||
# only apply on tweets
|
# only apply on tweets
|
||||||
type = Ticket::Article::Type.lookup( id: record.type_id )
|
type = Ticket::Article::Type.lookup( id: record.type_id )
|
||||||
return if type['name'] != 'twitter direct-message' && type['name'] != 'twitter status'
|
return if type['name'] !~ /\Atwitter/
|
||||||
|
|
||||||
a = Channel::TWITTER2.new
|
twitter = Channel::Twitter.new
|
||||||
message = a.send(
|
tweet = twitter.send({
|
||||||
{
|
|
||||||
type: type['name'],
|
type: type['name'],
|
||||||
to: record.to,
|
to: record.to,
|
||||||
body: record.body,
|
body: record.body,
|
||||||
in_reply_to: record.in_reply_to
|
in_reply_to: record.in_reply_to
|
||||||
},
|
})
|
||||||
#Rails.application.config.channel_twitter
|
record.message_id = tweet.id
|
||||||
)
|
|
||||||
record.message_id = message.id
|
|
||||||
record.save
|
record.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
226
lib/tweet.rb
Normal file
226
lib/tweet.rb
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
# Copyright (C) 2012-2015 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require 'twitter'
|
||||||
|
|
||||||
|
class Tweet
|
||||||
|
|
||||||
|
attr_accessor :client
|
||||||
|
|
||||||
|
def initialize(auth)
|
||||||
|
|
||||||
|
@client = Twitter::REST::Client.new do |config|
|
||||||
|
config.consumer_key = auth[:consumer_key]
|
||||||
|
config.consumer_secret = auth[:consumer_secret]
|
||||||
|
config.access_token = auth[:oauth_token]
|
||||||
|
config.access_token_secret = auth[:oauth_token_secret]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def disconnect
|
||||||
|
|
||||||
|
return if !@client
|
||||||
|
|
||||||
|
@client = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def user(tweet)
|
||||||
|
|
||||||
|
# status (full user data is included)
|
||||||
|
return tweet.user if tweet.respond_to?('user')
|
||||||
|
|
||||||
|
# direct message (full user data is included)
|
||||||
|
return tweet.sender if tweet.respond_to?('sender')
|
||||||
|
|
||||||
|
# search (no user data is included, do extra lookup)
|
||||||
|
begin
|
||||||
|
return @client.user(tweet.from_user_id) if tweet.respond_to?('from_user_id')
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "Twitter (#{tweet.id}): 'from_user_id' lookup error '#{e.inspect}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.error "Twitter (#{tweet.id}): unknown user source"
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_user(tweet)
|
||||||
|
|
||||||
|
Rails.logger.debug "Create user from tweet..."
|
||||||
|
Rails.logger.debug tweet.inspect
|
||||||
|
|
||||||
|
# do tweet_user lookup
|
||||||
|
tweet_user = user(tweet)
|
||||||
|
|
||||||
|
return if !tweet_user
|
||||||
|
|
||||||
|
auth = Authorization.find_by( uid: tweet_user.id, provider: 'twitter' )
|
||||||
|
|
||||||
|
# create or update user
|
||||||
|
user_data = {
|
||||||
|
login: tweet_user.screen_name,
|
||||||
|
firstname: tweet_user.name,
|
||||||
|
lastname: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
image_source: tweet_user.profile_image_url.to_s,
|
||||||
|
note: tweet_user.description,
|
||||||
|
active: true,
|
||||||
|
roles: Role.where( name: 'Customer' ),
|
||||||
|
}
|
||||||
|
if auth
|
||||||
|
user_data[:id] = auth.user_id
|
||||||
|
end
|
||||||
|
user = User.create_or_update( user_data )
|
||||||
|
|
||||||
|
# create or update authorization
|
||||||
|
auth_data = {
|
||||||
|
uid: tweet_user.id,
|
||||||
|
username: tweet_user.screen_name,
|
||||||
|
user_id: user.id,
|
||||||
|
provider: 'twitter'
|
||||||
|
}
|
||||||
|
if auth
|
||||||
|
auth.update_attributes( auth_data )
|
||||||
|
else
|
||||||
|
Authorization.new( auth_data )
|
||||||
|
end
|
||||||
|
|
||||||
|
UserInfo.current_user_id = user.id
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ticket(tweet, user, group)
|
||||||
|
|
||||||
|
Rails.logger.debug "Create ticket from tweet..."
|
||||||
|
Rails.logger.debug tweet.inspect
|
||||||
|
Rails.logger.debug user.inspect
|
||||||
|
Rails.logger.debug group.inspect
|
||||||
|
|
||||||
|
if tweet.class.to_s == 'Twitter::DirectMessage'
|
||||||
|
ticket = Ticket.find_by(
|
||||||
|
customer_id: user.id,
|
||||||
|
state: Ticket::State.where(
|
||||||
|
state_type_id: Ticket::StateType.where(
|
||||||
|
name: 'closed',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return ticket if ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
Ticket.create(
|
||||||
|
customer_id: user.id,
|
||||||
|
title: "#{tweet.text[0, 37]}...",
|
||||||
|
group: Group.find_by( name: group ),
|
||||||
|
state: Ticket::State.find_by( name: 'new' ),
|
||||||
|
priority: Ticket::Priority.find_by( name: '2 normal' ),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_article(tweet, user, ticket)
|
||||||
|
|
||||||
|
Rails.logger.debug "Create article from tweet..."
|
||||||
|
Rails.logger.debug tweet.inspect
|
||||||
|
Rails.logger.debug user.inspect
|
||||||
|
Rails.logger.debug ticket.inspect
|
||||||
|
|
||||||
|
# set ticket state to open if not new
|
||||||
|
if ticket.state.name != 'new'
|
||||||
|
ticket.state = Ticket::State.find_by( name: 'open' )
|
||||||
|
ticket.save
|
||||||
|
end
|
||||||
|
|
||||||
|
# import tweet
|
||||||
|
to = nil
|
||||||
|
if tweet.respond_to?('recipient')
|
||||||
|
to = tweet.recipient.name
|
||||||
|
end
|
||||||
|
|
||||||
|
article_type = 'twitter status'
|
||||||
|
if tweet.class.to_s == 'Twitter::DirectMessage'
|
||||||
|
article_type = 'twitter direct-message'
|
||||||
|
end
|
||||||
|
|
||||||
|
Ticket::Article.create(
|
||||||
|
from: user.login,
|
||||||
|
to: to,
|
||||||
|
body: tweet.text,
|
||||||
|
message_id: tweet.id,
|
||||||
|
ticket_id: ticket.id,
|
||||||
|
type: Ticket::Article::Type.find_by( name: article_type ),
|
||||||
|
sender: Ticket::Article::Sender.find_by( name: 'Customer' ),
|
||||||
|
internal: false,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_group(tweet, group)
|
||||||
|
|
||||||
|
Rails.logger.debug 'import tweet'
|
||||||
|
|
||||||
|
ticket = nil
|
||||||
|
# use transaction
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
|
||||||
|
# check if parent exists
|
||||||
|
user = to_user(tweet)
|
||||||
|
|
||||||
|
return if !user
|
||||||
|
|
||||||
|
if tweet.respond_to?('in_reply_to_status_id') && tweet.in_reply_to_status_id && tweet.in_reply_to_status_id.to_s != ''
|
||||||
|
|
||||||
|
existing_article = Ticket::Article.find_by( message_id: tweet.in_reply_to_status_id.to_s )
|
||||||
|
if existing_article
|
||||||
|
ticket = existing_article.ticket
|
||||||
|
else
|
||||||
|
Rails.logger.debug 'import in_reply_tweet ' + tweet.in_reply_to_status_id.to_s
|
||||||
|
|
||||||
|
parent_tweet = @client.status( tweet.in_reply_to_status_id )
|
||||||
|
ticket = to_group( parent_tweet, group )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ticket = to_ticket(tweet, user, group)
|
||||||
|
end
|
||||||
|
|
||||||
|
to_article(tweet, user, ticket)
|
||||||
|
|
||||||
|
# execute ticket events
|
||||||
|
Observer::Ticket::Notification.transaction
|
||||||
|
end
|
||||||
|
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_article(article)
|
||||||
|
|
||||||
|
|
||||||
|
tweet = nil
|
||||||
|
if article[:type] == 'twitter direct-message'
|
||||||
|
|
||||||
|
Rails.logger.debug "Create twitter direct message from article to '#{article[:to]}'..."
|
||||||
|
|
||||||
|
tweet = @client.create_direct_message(
|
||||||
|
article[:to],
|
||||||
|
article[:body],
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
elsif article[:type] == 'twitter status'
|
||||||
|
|
||||||
|
Rails.logger.debug "Create tweet from article..."
|
||||||
|
|
||||||
|
tweet = @client.update(
|
||||||
|
article[:body],
|
||||||
|
{
|
||||||
|
in_reply_to_status_id: article[:in_reply_to]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.debug tweet.inspect
|
||||||
|
tweet
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -20,32 +20,35 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
consumer_key = 'd2zoZBmMXmT7KLPgEHSzpw'
|
consumer_key = 'd2zoZBmMXmT7KLPgEHSzpw'
|
||||||
consumer_secret = 'QMUrlyDlqjITCkWdrOgsTxMVVLxr4A4IW3DIgtIg'
|
consumer_secret = 'QMUrlyDlqjITCkWdrOgsTxMVVLxr4A4IW3DIgtIg'
|
||||||
|
|
||||||
# user1: armin_theo (is system and is following marion_bauer)
|
# armin_theo (is system and is following marion_bauer)
|
||||||
user1_token = '1405469528-WQ6XHizgrbYasUwjp0I0TUONhftNkrfrpgFLrdc'
|
armin_theo_token = '1405469528-WQ6XHizgrbYasUwjp0I0TUONhftNkrfrpgFLrdc'
|
||||||
user1_token_secret = '0LHChGFlQx9jSxM8tkBsuDOMhbJMSXTL2zKJJO5Xk'
|
armin_theo_token_secret = '0LHChGFlQx9jSxM8tkBsuDOMhbJMSXTL2zKJJO5Xk'
|
||||||
|
|
||||||
# user2: me_bauer (is following armin_theo)
|
# me_bauer (is following armin_theo)
|
||||||
user2_token = '1406098795-XQTjg1Zj5uVW0C11NNpNA4xopyclRJJoriWis0I'
|
me_bauer_token = '1406098795-XQTjg1Zj5uVW0C11NNpNA4xopyclRJJoriWis0I'
|
||||||
user2_token_secret = 'T8ph5afeSDjGDA9X1ZBlzEvoSiXfN266ZZUMj5UaY'
|
me_bauer_token_secret = 'T8ph5afeSDjGDA9X1ZBlzEvoSiXfN266ZZUMj5UaY'
|
||||||
|
|
||||||
# add channel
|
# add channel
|
||||||
current = Channel.where( adapter: 'Twitter2' )
|
current = Channel.where( adapter: 'Twitter' )
|
||||||
current.each(&:destroy)
|
current.each(&:destroy)
|
||||||
Channel.create(
|
Channel.create(
|
||||||
adapter: 'Twitter2',
|
adapter: 'Twitter',
|
||||||
area: 'Twitter::Inbound',
|
area: 'Twitter::Inbound',
|
||||||
options: {
|
options: {
|
||||||
|
auth: {
|
||||||
consumer_key: consumer_key,
|
consumer_key: consumer_key,
|
||||||
consumer_secret: consumer_secret,
|
consumer_secret: consumer_secret,
|
||||||
oauth_token: user1_token,
|
oauth_token: armin_theo_token,
|
||||||
oauth_token_secret: user1_token_secret,
|
oauth_token_secret: armin_theo_token_secret,
|
||||||
|
},
|
||||||
|
sync: {
|
||||||
search: [
|
search: [
|
||||||
{
|
{
|
||||||
item: '#citheo42',
|
term: '#citheo42',
|
||||||
group: 'Twitter',
|
group: 'Twitter',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
item: '#citheo24',
|
term: '#citheo24',
|
||||||
group: 'Users',
|
group: 'Users',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -55,6 +58,7 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
direct_messages: {
|
direct_messages: {
|
||||||
group: 'Twitter',
|
group: 'Twitter',
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
active: true,
|
active: true,
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
|
@ -63,51 +67,49 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'new outbound and reply' do
|
test 'new outbound and reply' do
|
||||||
|
|
||||||
user = User.find(2)
|
|
||||||
group = Group.where( name: 'Twitter' ).first
|
|
||||||
state = Ticket::State.where( name: 'new' ).first
|
|
||||||
priority = Ticket::Priority.where( name: '2 normal' ).first
|
|
||||||
hash = '#citheo42' + rand(9999).to_s
|
hash = '#citheo42' + rand(9999).to_s
|
||||||
text = 'Today the weather is really nice... ' + hash
|
user = User.find(2)
|
||||||
|
text = "Today the weather is really nice... #{hash}"
|
||||||
ticket = Ticket.create(
|
ticket = Ticket.create(
|
||||||
group_id: group.id,
|
|
||||||
customer_id: user.id,
|
|
||||||
title: text[0, 40],
|
title: text[0, 40],
|
||||||
state_id: state.id,
|
customer_id: user.id,
|
||||||
priority_id: priority.id,
|
group: Group.find_by( name: 'Twitter' ),
|
||||||
|
state: Ticket::State.find_by( name: 'new' ),
|
||||||
|
priority: Ticket::Priority.find_by( name: '2 normal' ),
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
)
|
)
|
||||||
assert( ticket, 'outbound ticket created' )
|
assert( ticket, "outbound ticket created, text: #{text}" )
|
||||||
|
|
||||||
article = Ticket::Article.create(
|
article = Ticket::Article.create(
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
type_id: Ticket::Article::Type.where( name: 'twitter status' ).first.id,
|
|
||||||
sender_id: Ticket::Article::Sender.where( name: 'Agent' ).first.id,
|
|
||||||
body: text,
|
body: text,
|
||||||
#:from => sender.name,
|
type: Ticket::Article::Type.find_by( name: 'twitter status' ),
|
||||||
#:to => to,
|
sender: Ticket::Article::Sender.find_by( name: 'Agent' ),
|
||||||
#:message_id => tweet.id,
|
|
||||||
internal: false,
|
internal: false,
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
)
|
)
|
||||||
assert( article, 'outbound article created' )
|
assert( article, "outbound article created, text: #{text}" )
|
||||||
assert_equal( article.ticket.articles.count, 1 )
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# reply by me_bauer
|
# reply by me_bauer
|
||||||
client = Twitter::REST::Client.new do |config|
|
client = Twitter::REST::Client.new do |config|
|
||||||
config.consumer_key = consumer_key
|
config.consumer_key = consumer_key
|
||||||
config.consumer_secret = consumer_secret
|
config.consumer_secret = consumer_secret
|
||||||
config.access_token = user2_token
|
config.access_token = me_bauer_token
|
||||||
config.access_token_secret = user2_token_secret
|
config.access_token_secret = me_bauer_token_secret
|
||||||
end
|
|
||||||
client.search(hash, count: 50, result_type: 'recent').collect do |tweet|
|
|
||||||
assert_equal( tweet.id, article.message_id )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
reply_hash = '#weather' + rand(9999).to_s
|
tweet_found = false
|
||||||
reply_text = '@armin_theo on my side the weather is also nice! 😍😍😍 ' + reply_hash
|
client.user_timeline('armin_theo').each { |tweet|
|
||||||
|
|
||||||
|
next if tweet.id != article.message_id
|
||||||
|
tweet_found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
assert( tweet_found, "found outbound '#{text}' tweet '#{article.message_id}'" )
|
||||||
|
|
||||||
|
reply_text = '@armin_theo on my side the weather is nice, too! 😍😍😍 #weather' + rand(9999).to_s
|
||||||
tweet = client.update(
|
tweet = client.update(
|
||||||
reply_text,
|
reply_text,
|
||||||
{
|
{
|
||||||
|
@ -115,43 +117,40 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# fetch check system account
|
# fetch check system account
|
||||||
Channel.fetch
|
Channel.fetch
|
||||||
|
|
||||||
# check if follow up article has been created
|
# check if follow up article has been created
|
||||||
assert_equal( article.ticket.articles.count, 2 )
|
article = Ticket::Article.find_by( message_id: tweet.id )
|
||||||
reply_article = article.ticket.articles.last
|
|
||||||
assert_equal( reply_article.body, reply_text.utf8_to_3bytesutf8 )
|
|
||||||
|
|
||||||
|
assert( article, "article tweet '#{tweet.id}' imported" )
|
||||||
|
assert_equal( 2, article.ticket.articles.count, 'ticket article inbound count' )
|
||||||
|
assert_equal( reply_text.utf8_to_3bytesutf8, ticket.articles.last.body, 'ticket article inbound body' )
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'new by direct message inbound' do
|
test 'new by direct message inbound' do
|
||||||
|
|
||||||
# cleanup direct messages of system
|
# cleanup direct messages of system
|
||||||
client = Twitter::REST::Client.new do |config|
|
client = Twitter::REST::Client.new do |config|
|
||||||
config.consumer_key = consumer_key
|
config.consumer_key = consumer_key
|
||||||
config.consumer_secret = consumer_secret
|
config.consumer_secret = consumer_secret
|
||||||
config.access_token = user1_token
|
config.access_token = armin_theo_token
|
||||||
config.access_token_secret = user1_token_secret
|
config.access_token_secret = armin_theo_token_secret
|
||||||
end
|
end
|
||||||
dms = client.direct_messages( count: 200 )
|
dms = client.direct_messages( count: 200 )
|
||||||
dms.each {|dm|
|
dms.each {|dm|
|
||||||
client.destroy_direct_message(dm.id)
|
client.destroy_direct_message(dm.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
# direct message to @armin_theo
|
|
||||||
client = Twitter::REST::Client.new(
|
client = Twitter::REST::Client.new(
|
||||||
consumer_key: consumer_key,
|
consumer_key: consumer_key,
|
||||||
consumer_secret: consumer_secret,
|
consumer_secret: consumer_secret,
|
||||||
access_token: user2_token,
|
access_token: me_bauer_token,
|
||||||
access_token_secret: user2_token_secret
|
access_token_secret: me_bauer_token_secret
|
||||||
)
|
)
|
||||||
dms = client.direct_messages( count: 200 )
|
dms = client.direct_messages( count: 200 )
|
||||||
dms.each {|dm|
|
dms.each {|dm|
|
||||||
client.destroy_direct_message(dm.id)
|
client.destroy_direct_message(dm.id)
|
||||||
}
|
}
|
||||||
sleep 10
|
|
||||||
|
|
||||||
hash = '#citheo44' + rand(9999).to_s
|
hash = '#citheo44' + rand(9999).to_s
|
||||||
text = 'How about the details? ' + hash
|
text = 'How about the details? ' + hash
|
||||||
|
@ -164,43 +163,35 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
# fetch check system account
|
# fetch check system account
|
||||||
article = nil
|
article = nil
|
||||||
(1..4).each {
|
(1..4).each {
|
||||||
next if article
|
|
||||||
sleep 25
|
|
||||||
|
|
||||||
Channel.fetch
|
Channel.fetch
|
||||||
|
|
||||||
# check if ticket and article has been created
|
# check if ticket and article has been created
|
||||||
article = Ticket::Article.where( message_id: dm.id ).last
|
article = Ticket::Article.find_by( message_id: dm.id )
|
||||||
|
|
||||||
|
break if article
|
||||||
|
|
||||||
|
sleep 5
|
||||||
}
|
}
|
||||||
puts '----------------------------------------'
|
|
||||||
puts 'DM: ' + dm.inspect
|
|
||||||
puts 'AT: ' + article.inspect
|
|
||||||
puts '----------------------------------------'
|
|
||||||
|
|
||||||
assert( article, 'inbound article created' )
|
assert( article, 'inbound article created' )
|
||||||
#ticket = Ticket.find( article.ticket.id )
|
|
||||||
ticket = article.ticket
|
ticket = article.ticket
|
||||||
assert( ticket, 'ticket of inbound article exists' )
|
assert( ticket, 'ticket of inbound article exists' )
|
||||||
assert( ticket.articles, 'ticket.articles exists' )
|
assert( ticket.articles, 'ticket.articles exists' )
|
||||||
article_count = ticket.articles.count
|
assert_equal( ticket.articles.count, 1, 'ticket article inbound count' )
|
||||||
assert( article_count )
|
assert_equal( ticket.state.name, 'new' )
|
||||||
#assert_equal( ticket.state.name, 'new' )
|
|
||||||
|
|
||||||
# reply via ticket
|
# reply via ticket
|
||||||
outbound_article = Ticket::Article.create(
|
outbound_article = Ticket::Article.create(
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
type_id: Ticket::Article::Type.where( name: 'twitter direct-message' ).first.id,
|
|
||||||
sender_id: Ticket::Article::Sender.where( name: 'Agent' ).first.id,
|
|
||||||
body: text,
|
|
||||||
#:from => sender.name,
|
|
||||||
to: 'me_bauer',
|
to: 'me_bauer',
|
||||||
|
body: text,
|
||||||
|
type: Ticket::Article::Type.find_by( name: 'twitter direct-message' ),
|
||||||
|
sender: Ticket::Article::Sender.find_by( name: 'Agent' ),
|
||||||
internal: false,
|
internal: false,
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
)
|
)
|
||||||
assert( outbound_article, 'outbound article created' )
|
assert( outbound_article, 'outbound article created' )
|
||||||
assert_equal( outbound_article.ticket.articles.count, article_count + 1 )
|
assert_equal( outbound_article.ticket.articles.count, 2, 'ticket article outbound count' )
|
||||||
sleep 10
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue