Refactoring: Twitter webhook spec.
This commit is contained in:
parent
7326122e28
commit
2077fac218
7 changed files with 899 additions and 740 deletions
|
@ -9,19 +9,23 @@ FactoryBot.define do
|
||||||
created_by_id 1
|
created_by_id 1
|
||||||
|
|
||||||
factory :twitter_channel do
|
factory :twitter_channel do
|
||||||
|
transient do
|
||||||
|
custom_options { {} }
|
||||||
|
end
|
||||||
|
|
||||||
area 'Twitter::Account'
|
area 'Twitter::Account'
|
||||||
options do
|
options do
|
||||||
{
|
{
|
||||||
adapter: 'twitter',
|
adapter: 'twitter',
|
||||||
auth: {
|
auth: {
|
||||||
consumer_key: 'some',
|
consumer_key: 'some',
|
||||||
consumer_secret: 'some',
|
consumer_secret: 'some',
|
||||||
oauth_token: 'key',
|
oauth_token: 'key',
|
||||||
oauth_token_secret: 'secret',
|
oauth_token_secret: 'secret',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
screen_name: 'system_login',
|
|
||||||
id: 'system_id',
|
id: 'system_id',
|
||||||
|
screen_name: 'system_login',
|
||||||
},
|
},
|
||||||
sync: {
|
sync: {
|
||||||
import_older_tweets: true,
|
import_older_tweets: true,
|
||||||
|
@ -29,21 +33,21 @@ FactoryBot.define do
|
||||||
search: [
|
search: [
|
||||||
{
|
{
|
||||||
term: 'zammad',
|
term: 'zammad',
|
||||||
group_id: Group.first.id,
|
group_id: Group.first.id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
term: 'hash_tag1',
|
term: 'hash_tag1',
|
||||||
group_id: Group.first.id,
|
group_id: Group.first.id
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
mentions: {
|
mentions: {
|
||||||
group_id: Group.first.id,
|
group_id: Group.first.id
|
||||||
},
|
},
|
||||||
direct_messages: {
|
direct_messages: {
|
||||||
group_id: Group.first.id,
|
group_id: Group.first.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.deep_merge(custom_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,282 +1,412 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe 'Integration Twitter Webhook', type: :request do
|
RSpec.describe 'Twitter Webhook Integration', type: :request do
|
||||||
|
let!(:external_credential) { create(:twitter_credential, credentials: credentials) }
|
||||||
|
let(:credentials) { { consumer_key: 'CCC', consumer_secret: 'DDD' } }
|
||||||
|
|
||||||
let(:agent_user) do
|
describe '#webhook_verify (for Twitter to confirm Zammad’s credentials)' do
|
||||||
create(:agent_user)
|
context 'with only cached credentials' do
|
||||||
|
let!(:external_credential) { nil }
|
||||||
|
before { Cache.write('external_credential_twitter', credentials) }
|
||||||
|
|
||||||
|
it 'returns an HMAC signature for cached credentials plus params[:crc_token]' do
|
||||||
|
get '/api/v1/channels_twitter_webhook',
|
||||||
|
params: { crc_token: 'some_random', nonce: 'some_nonce' },
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'something' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response).to include('response_token' => 'sha256=VE19eUk6krbdSqWPdvH71xtFhApBAU81uPW3UT65vOs=')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with only credentials stored in DB' do
|
||||||
|
it 'returns an HMAC signature for stored credentials plus params[:crc_token]' do
|
||||||
|
get '/api/v1/channels_twitter_webhook',
|
||||||
|
params: { crc_token: 'some_random', nonce: 'some_nonce' },
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'something' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response).to include('response_token' => 'sha256=VE19eUk6krbdSqWPdvH71xtFhApBAU81uPW3UT65vOs=')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:each) do
|
describe '#webhook_incoming' do
|
||||||
@external_credential = ExternalCredential.create!(
|
let!(:channel) do
|
||||||
name: 'twitter',
|
create(
|
||||||
credentials: {
|
:twitter_channel,
|
||||||
consumer_key: 'CCC',
|
custom_options: {
|
||||||
consumer_secret: 'DDD',
|
auth: {
|
||||||
}
|
external_credential_id: external_credential.id,
|
||||||
)
|
oauth_token: 'AAA',
|
||||||
@channel = Channel.create!(
|
oauth_token_secret: 'BBB',
|
||||||
group_id: nil,
|
consumer_key: 'CCC',
|
||||||
area: 'Twitter::Account',
|
consumer_secret: 'DDD',
|
||||||
options: {
|
},
|
||||||
adapter: 'twitter',
|
user: {
|
||||||
user: {
|
id: 123,
|
||||||
id: 123,
|
name: 'Zammad HQ',
|
||||||
screen_name: 'zammadhq',
|
screen_name: 'zammadhq',
|
||||||
name: 'Zammad HQ',
|
},
|
||||||
},
|
sync: {
|
||||||
auth: {
|
limit: 20,
|
||||||
external_credential_id: 1,
|
track_retweets: false,
|
||||||
oauth_token: 'AAA',
|
search: [
|
||||||
oauth_token_secret: 'BBB',
|
{
|
||||||
consumer_key: 'CCC',
|
term: '#zammad', group_id: Group.first.id.to_s
|
||||||
consumer_secret: 'DDD',
|
},
|
||||||
},
|
{
|
||||||
sync: {
|
term: '#hello1234', group_id: Group.first.id.to_s
|
||||||
limit: 20,
|
}
|
||||||
search: [{ term: '#zammad', group_id: Group.first.id.to_s }, { term: '#hello1234', group_id: Group.first.id.to_s }],
|
],
|
||||||
mentions: { group_id: Group.first.id.to_s },
|
}
|
||||||
direct_messages: { group_id: Group.first.id.to_s },
|
|
||||||
track_retweets: false
|
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
active: true,
|
|
||||||
updated_by_id: 1,
|
|
||||||
created_by_id: 1,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'request verify' do
|
|
||||||
|
|
||||||
it 'does at config check' do
|
|
||||||
Cache.write('external_credential_twitter', @external_credential.credentials)
|
|
||||||
@external_credential.destroy
|
|
||||||
params = {
|
|
||||||
crc_token: 'some_random',
|
|
||||||
nonce: 'some_nonce',
|
|
||||||
}
|
|
||||||
get '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'something' }, as: :json
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
expect(json_response['response_token']).to eq('sha256=VE19eUk6krbdSqWPdvH71xtFhApBAU81uPW3UT65vOs=')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does configured check' do
|
describe 'confirming authenticity of incoming Twitter webhook' do
|
||||||
Cache.delete('external_credential_twitter')
|
context 'with valid headers & parameters' do
|
||||||
params = {
|
it 'returns 200 success' do
|
||||||
crc_token: 'some_random',
|
post '/api/v1/channels_twitter_webhook',
|
||||||
nonce: 'some_nonce',
|
params: { for_user_id: channel.options[:user][:id], key: 'value' },
|
||||||
}
|
headers: { 'x-twitter-webhooks-signature' => 'sha256=JjEmBe1lVKT8XldrYUKibF+D5ehp8f0jDk3PXZSHEWI=' },
|
||||||
get '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'something' }, as: :json
|
as: :json
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
expect(json_response['response_token']).to eq('sha256=VE19eUk6krbdSqWPdvH71xtFhApBAU81uPW3UT65vOs=')
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when request is missing x-twitter-webhooks-signature header' do
|
||||||
|
it 'returns 422 with error message' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(json_response).to include('error' => "Missing 'x-twitter-webhooks-signature' header")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Zammad has no Twitter credentials (in DB or cache)' do
|
||||||
|
let!(:external_credential) { nil }
|
||||||
|
let!(:channel) { nil }
|
||||||
|
|
||||||
|
it 'returns 422 with error message' do
|
||||||
|
post '/api/v1/channels_twitter_webhook',
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'something' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(json_response).to include('error' => "No such external_credential 'twitter'!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid token in x-twitter-webhooks-signature header' do
|
||||||
|
it 'returns 401 with error message' do
|
||||||
|
post '/api/v1/channels_twitter_webhook',
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'something' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(401)
|
||||||
|
expect(json_response).to include('error' => 'Not authorized')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with :for_user_id request parameter' do
|
||||||
|
it 'returns 422 with error message' do
|
||||||
|
post '/api/v1/channels_twitter_webhook',
|
||||||
|
params: { key: 'value' },
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'sha256=EERHBy/k17v+SuT+K0OXuwhJtKnPtxi0n/Y4Wye4kVU=' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(json_response).to include('error' => "Missing 'for_user_id' in payload!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid :for_user_id request parameter' do
|
||||||
|
it 'returns 422 with error message' do
|
||||||
|
post '/api/v1/channels_twitter_webhook',
|
||||||
|
params: { for_user_id: 'not_existing', key: 'value' },
|
||||||
|
headers: { 'x-twitter-webhooks-signature' => 'sha256=QaJiQl/4WRp/GF37b+eAdF6kPgptjDCLOgAIIbB1s0I=' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(json_response).to include('error' => "No such channel for user id 'not_existing'!")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
describe 'auto-creation of tickets/articles on webhook receipt' do
|
||||||
|
let(:webhook_payload) do
|
||||||
|
JSON.parse(File.read(Rails.root.join('test', 'data', 'twitter', payload_file))).symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
describe 'request incoming - base' do
|
context 'for outbound DMs' do
|
||||||
|
context 'not matching any admin-defined filters' do
|
||||||
|
let(:payload_file) { 'webhook1_direct_message.json' }
|
||||||
|
|
||||||
it 'does without x-twitter-webhooks-signature header check' do
|
it 'returns 200' do
|
||||||
params = {}
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, as: :json
|
|
||||||
expect(response).to have_http_status(422)
|
expect(response).to have_http_status(200)
|
||||||
expect(json_response['error']).to eq('Missing \'x-twitter-webhooks-signature\' header')
|
end
|
||||||
|
|
||||||
|
it 'creates closed ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'title' => 'Hey! Hello!',
|
||||||
|
'state_id' => Ticket::State.find_by(name: 'closed').id,
|
||||||
|
'priority_id' => Ticket::Priority.find_by(name: '2 normal').id,
|
||||||
|
'customer_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id,
|
||||||
|
'created_by_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates first article on closed ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket::Article.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'from' => '@zammadhq',
|
||||||
|
'to' => '@medenhofer',
|
||||||
|
'message_id' => '1062015437679050760',
|
||||||
|
'created_by_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add any attachments to newly created ticket' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attachments).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for inbound DMs' do
|
||||||
|
context 'matching admin-defined #hashtag filter, with a link to an image' do
|
||||||
|
let(:payload_file) { 'webhook2_direct_message.json' }
|
||||||
|
|
||||||
|
it 'returns 200' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates new ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'title' => 'Hello Zammad #zammad @znuny Yeah! https://t.co/UfaCwi9cUb',
|
||||||
|
'state_id' => Ticket::State.find_by(name: 'new').id,
|
||||||
|
'priority_id' => Ticket::Priority.find_by(name: '2 normal').id,
|
||||||
|
'customer_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates first article on new ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket::Article.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'to' => '@zammadhq',
|
||||||
|
'from' => '@medenhofer',
|
||||||
|
'body' => "Hello Zammad #zammad @znuny\n\nYeah! https://twitter.com/messages/media/1063077238797725700",
|
||||||
|
'message_id' => '1063077238797725700',
|
||||||
|
'created_by_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add linked image as attachment to newly created ticket' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attachments).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'from same sender as previously imported DMs' do
|
||||||
|
let(:payload_file) { 'webhook3_direct_message.json' }
|
||||||
|
|
||||||
|
before { post '/api/v1/channels_twitter_webhook', **previous_webhook_payload, as: :json }
|
||||||
|
|
||||||
|
let(:previous_webhook_payload) do
|
||||||
|
JSON.parse(File.read(Rails.root.join('test', 'data', 'twitter', 'webhook2_direct_message.json'))).symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 200' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create new ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.not_to change { Ticket.count }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds new article to existing, open ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket::Article.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'to' => '@zammadhq',
|
||||||
|
'from' => '@medenhofer',
|
||||||
|
'body' => 'Hello again!',
|
||||||
|
'message_id' => '1063077238797725701',
|
||||||
|
'created_by_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id,
|
||||||
|
'ticket_id' => Ticket.find_by(title: 'Hello Zammad #zammad @znuny Yeah! https://t.co/UfaCwi9cUb').id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add any attachments to newly created ticket' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attachments).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when receiving duplicate DMs' do
|
||||||
|
let(:payload_file) { 'webhook1_direct_message.json' }
|
||||||
|
|
||||||
|
it 'still returns 200' do
|
||||||
|
2.times { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create duplicate articles' do
|
||||||
|
expect do
|
||||||
|
2.times { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
end.to change { Ticket::Article.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for tweets' do
|
||||||
|
context 'matching admin-defined #hashtag filter, with an image link' do
|
||||||
|
let(:payload_file) { 'webhook1_tweet.json' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:get, 'http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_bigger.jpg')
|
||||||
|
.to_return(status: 200, body: 'some_content')
|
||||||
|
|
||||||
|
stub_request(:get, 'https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg')
|
||||||
|
.to_return(status: 200, body: 'some_content')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 200' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a closed ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'title' => 'Hey @medenhofer ! #hello1234 https://t.co/f1kffFlwpN',
|
||||||
|
'state_id' => Ticket::State.find_by(name: 'closed').id,
|
||||||
|
'priority_id' => Ticket::Priority.find_by(name: '2 normal').id,
|
||||||
|
'customer_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id,
|
||||||
|
'created_by_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates first article on closed ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket::Article.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'from' => '@zammadhq',
|
||||||
|
'to' => '@medenhofer',
|
||||||
|
'body' => 'Hey @medenhofer ! #hello1234 https://twitter.com/zammadhq/status/1063212927510081536/photo/1',
|
||||||
|
'message_id' => '1063212927510081536',
|
||||||
|
'created_by_id' => User.find_by(login: 'zammadhq', firstname: 'Zammad', lastname: 'Hq').id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'add linked image as attachment to newly created article' do
|
||||||
|
expect(Ticket::Article.last.attachments)
|
||||||
|
.to match_array(Store.where(filename: 'DsFKfJRWkAAFEbo.jpg'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'longer than 140 characters (with no media links)' do
|
||||||
|
let(:payload_file) { 'webhook2_tweet.json' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:get, 'http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_bigger.jpg')
|
||||||
|
.to_return(status: 200, body: 'some_content')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 200' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'title' => '@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy ...',
|
||||||
|
'state_id' => Ticket::State.find_by(name: 'new').id,
|
||||||
|
'priority_id' => Ticket::Priority.find_by(name: '2 normal').id,
|
||||||
|
'customer_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id,
|
||||||
|
'created_by_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates first article on new ticket' do
|
||||||
|
expect { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
.to change { Ticket::Article.count }.by(1)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attributes)
|
||||||
|
.to include(
|
||||||
|
'from' => '@medenhofer',
|
||||||
|
'to' => '@znuny',
|
||||||
|
'body' => '@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lore',
|
||||||
|
'created_by_id' => User.find_by(login: 'medenhofer', firstname: 'Martin', lastname: 'Edenhofer').id,
|
||||||
|
'message_id' => '1065035365336141825'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add any attachments to newly created ticket' do
|
||||||
|
post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.attachments).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when receiving duplicate messages' do
|
||||||
|
let(:payload_file) { 'webhook1_tweet.json' }
|
||||||
|
|
||||||
|
it 'still returns 200' do
|
||||||
|
2.times { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create duplicate articles' do
|
||||||
|
expect do
|
||||||
|
2.times { post '/api/v1/channels_twitter_webhook', **webhook_payload, as: :json }
|
||||||
|
end.to change { Ticket::Article.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does no external_credential check' do
|
|
||||||
@external_credential.destroy
|
|
||||||
params = {}
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'something' }, as: :json
|
|
||||||
expect(response).to have_http_status(422)
|
|
||||||
expect(json_response['error']).to eq('No such external_credential \'twitter\'!')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does invalid token check' do
|
|
||||||
params = {}
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'something' }, as: :json
|
|
||||||
expect(response).to have_http_status(401)
|
|
||||||
expect(json_response['error']).to eq('Not authorized')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does existing for_user_id check' do
|
|
||||||
params = { key: 'value' }
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'sha256=EERHBy/k17v+SuT+K0OXuwhJtKnPtxi0n/Y4Wye4kVU=' }, as: :json
|
|
||||||
expect(response).to have_http_status(422)
|
|
||||||
expect(json_response['error']).to eq('Missing \'for_user_id\' in payload!')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does invalid user check' do
|
|
||||||
params = { for_user_id: 'not_existing', key: 'value' }
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'sha256=QaJiQl/4WRp/GF37b+eAdF6kPgptjDCLOgAIIbB1s0I=' }, as: :json
|
|
||||||
expect(response).to have_http_status(422)
|
|
||||||
expect(json_response['error']).to eq('No such channel for user id \'not_existing\'!')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does valid token check' do
|
|
||||||
params = { for_user_id: 123, key: 'value' }
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: params, headers: { 'x-twitter-webhooks-signature' => 'sha256=JjEmBe1lVKT8XldrYUKibF+D5ehp8f0jDk3PXZSHEWI=' }, as: :json
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'request incoming direct message' do
|
|
||||||
it 'create new ticket via tweet' do
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_direct_message'), headers: { 'x-twitter-webhooks-signature' => 'sha256=xXu7qrPhqXfo8Ot14c0si9HrdQdBNru5fkSdoMZi+Ms=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
article = Ticket::Article.find_by(message_id: '1062015437679050760')
|
|
||||||
expect(article).to be_present
|
|
||||||
expect(article.from).to eq('@zammadhq')
|
|
||||||
expect(article.to).to eq('@medenhofer')
|
|
||||||
expect(article.created_by.login).to eq('zammadhq')
|
|
||||||
expect(article.created_by.firstname).to eq('Zammad')
|
|
||||||
expect(article.created_by.lastname).to eq('Hq')
|
|
||||||
expect(article.attachments.count).to eq(0)
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
expect(ticket.title).to eq('Hey! Hello!')
|
|
||||||
expect(ticket.state.name).to eq('closed')
|
|
||||||
expect(ticket.priority.name).to eq('2 normal')
|
|
||||||
expect(ticket.customer.login).to eq('zammadhq')
|
|
||||||
expect(ticket.customer.firstname).to eq('Zammad')
|
|
||||||
expect(ticket.customer.lastname).to eq('Hq')
|
|
||||||
expect(ticket.created_by.login).to eq('zammadhq')
|
|
||||||
expect(ticket.created_by.firstname).to eq('Zammad')
|
|
||||||
expect(ticket.created_by.lastname).to eq('Hq')
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook2_direct_message'), headers: { 'x-twitter-webhooks-signature' => 'sha256=wYiCk7gfAgrnerCpj3XD58hozfVDjcQvcYPZCFH+stU=' }, as: :json
|
|
||||||
|
|
||||||
article = Ticket::Article.find_by(message_id: '1063077238797725700')
|
|
||||||
expect(article).to be_present
|
|
||||||
expect(article.to).to eq('@zammadhq')
|
|
||||||
expect(article.from).to eq('@medenhofer')
|
|
||||||
expect(article.body).to eq("Hello Zammad #zammad @znuny\n\nYeah! https://twitter.com/messages/media/1063077238797725700")
|
|
||||||
expect(article.created_by.login).to eq('medenhofer')
|
|
||||||
expect(article.created_by.firstname).to eq('Martin')
|
|
||||||
expect(article.created_by.lastname).to eq('Edenhofer')
|
|
||||||
expect(article.attachments.count).to eq(0)
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
expect(ticket.title).to eq('Hello Zammad #zammad @znuny Yeah! https://t.co/UfaCwi9cUb')
|
|
||||||
expect(ticket.state.name).to eq('new')
|
|
||||||
expect(ticket.priority.name).to eq('2 normal')
|
|
||||||
expect(ticket.customer.login).to eq('medenhofer')
|
|
||||||
expect(ticket.customer.firstname).to eq('Martin')
|
|
||||||
expect(ticket.customer.lastname).to eq('Edenhofer')
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook3_direct_message'), headers: { 'x-twitter-webhooks-signature' => 'sha256=OTguUdchBdxNal/csZsRkytKL5srrUuezZ3hp/2E404=' }, as: :json
|
|
||||||
|
|
||||||
article = Ticket::Article.find_by(message_id: '1063077238797725701')
|
|
||||||
expect(article).to be_present
|
|
||||||
expect(article.to).to eq('@zammadhq')
|
|
||||||
expect(article.from).to eq('@medenhofer')
|
|
||||||
expect(article.body).to eq('Hello again!')
|
|
||||||
expect(article.created_by.login).to eq('medenhofer')
|
|
||||||
expect(article.created_by.firstname).to eq('Martin')
|
|
||||||
expect(article.created_by.lastname).to eq('Edenhofer')
|
|
||||||
expect(article.ticket.id).to eq(ticket.id)
|
|
||||||
expect(article.attachments.count).to eq(0)
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
expect(ticket.title).to eq('Hello Zammad #zammad @znuny Yeah! https://t.co/UfaCwi9cUb')
|
|
||||||
expect(ticket.state.name).to eq('new')
|
|
||||||
expect(ticket.priority.name).to eq('2 normal')
|
|
||||||
expect(ticket.customer.login).to eq('medenhofer')
|
|
||||||
expect(ticket.customer.firstname).to eq('Martin')
|
|
||||||
expect(ticket.customer.lastname).to eq('Edenhofer')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'check duplicate' do
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_direct_message'), headers: { 'x-twitter-webhooks-signature' => 'sha256=xXu7qrPhqXfo8Ot14c0si9HrdQdBNru5fkSdoMZi+Ms=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_direct_message'), headers: { 'x-twitter-webhooks-signature' => 'sha256=xXu7qrPhqXfo8Ot14c0si9HrdQdBNru5fkSdoMZi+Ms=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
|
|
||||||
expect(Ticket::Article.where(message_id: '1062015437679050760').count).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'request incoming direct message' do
|
|
||||||
|
|
||||||
it 'create new ticket via tweet' do
|
|
||||||
|
|
||||||
stub_request(:get, 'http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_bigger.jpg')
|
|
||||||
.to_return(status: 200, body: 'some_content')
|
|
||||||
|
|
||||||
stub_request(:get, 'https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg')
|
|
||||||
.to_return(status: 200, body: 'some_content')
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_tweet'), headers: { 'x-twitter-webhooks-signature' => 'sha256=DmARpz6wdgte6Vj+ePeqC+RHvEDokmwOIIqr4//utkk=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
article = Ticket::Article.find_by(message_id: '1063212927510081536')
|
|
||||||
expect(article).to be_present
|
|
||||||
expect(article.from).to eq('@zammadhq')
|
|
||||||
expect(article.to).to eq('@medenhofer')
|
|
||||||
expect(article.body).to eq('Hey @medenhofer ! #hello1234 https://twitter.com/zammadhq/status/1063212927510081536/photo/1')
|
|
||||||
expect(article.created_by.login).to eq('zammadhq')
|
|
||||||
expect(article.created_by.firstname).to eq('Zammad')
|
|
||||||
expect(article.created_by.lastname).to eq('Hq')
|
|
||||||
expect(article.attachments.count).to eq(1)
|
|
||||||
expect(article.attachments[0].filename).to eq('DsFKfJRWkAAFEbo.jpg')
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
expect(ticket.title).to eq('Hey @medenhofer ! #hello1234 https://t.co/f1kffFlwpN')
|
|
||||||
expect(ticket.state.name).to eq('closed')
|
|
||||||
expect(ticket.priority.name).to eq('2 normal')
|
|
||||||
expect(ticket.customer.login).to eq('zammadhq')
|
|
||||||
expect(ticket.customer.firstname).to eq('Zammad')
|
|
||||||
expect(ticket.customer.lastname).to eq('Hq')
|
|
||||||
expect(ticket.created_by.login).to eq('zammadhq')
|
|
||||||
expect(ticket.created_by.firstname).to eq('Zammad')
|
|
||||||
expect(ticket.created_by.lastname).to eq('Hq')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'create new ticket via tweet extended_tweet' do
|
|
||||||
|
|
||||||
stub_request(:get, 'http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_bigger.jpg')
|
|
||||||
.to_return(status: 200, body: 'some_content')
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook2_tweet'), headers: { 'x-twitter-webhooks-signature' => 'sha256=U7bglX19JitI2xuvyONAc0d/fowIFEeUzkEgnWdGyUM=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
article = Ticket::Article.find_by(message_id: '1065035365336141825')
|
|
||||||
expect(article).to be_present
|
|
||||||
expect(article.from).to eq('@medenhofer')
|
|
||||||
expect(article.to).to eq('@znuny')
|
|
||||||
expect(article.body).to eq('@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lore')
|
|
||||||
expect(article.created_by.login).to eq('medenhofer')
|
|
||||||
expect(article.created_by.firstname).to eq('Martin')
|
|
||||||
expect(article.created_by.lastname).to eq('Edenhofer')
|
|
||||||
expect(article.attachments.count).to eq(0)
|
|
||||||
|
|
||||||
ticket = article.ticket
|
|
||||||
expect(ticket.title).to eq('@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy ...')
|
|
||||||
expect(ticket.state.name).to eq('new')
|
|
||||||
expect(ticket.priority.name).to eq('2 normal')
|
|
||||||
expect(ticket.customer.login).to eq('medenhofer')
|
|
||||||
expect(ticket.customer.firstname).to eq('Martin')
|
|
||||||
expect(ticket.customer.lastname).to eq('Edenhofer')
|
|
||||||
expect(ticket.created_by.login).to eq('medenhofer')
|
|
||||||
expect(ticket.created_by.firstname).to eq('Martin')
|
|
||||||
expect(ticket.created_by.lastname).to eq('Edenhofer')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'check duplicate' do
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_tweet'), headers: { 'x-twitter-webhooks-signature' => 'sha256=DmARpz6wdgte6Vj+ePeqC+RHvEDokmwOIIqr4//utkk=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
|
|
||||||
post '/api/v1/channels_twitter_webhook', params: read_messaage('webhook1_tweet'), headers: { 'x-twitter-webhooks-signature' => 'sha256=DmARpz6wdgte6Vj+ePeqC+RHvEDokmwOIIqr4//utkk=' }, as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
|
|
||||||
expect(Ticket::Article.where(message_id: '1063212927510081536').count).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_messaage(file)
|
|
||||||
JSON.parse(File.read(Rails.root.join('test', 'data', 'twitter', "#{file}.json")))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,65 +1,70 @@
|
||||||
{
|
{
|
||||||
"for_user_id": "123",
|
"headers" : {
|
||||||
"direct_message_events": [
|
"x-twitter-webhooks-signature" : "sha256=xXu7qrPhqXfo8Ot14c0si9HrdQdBNru5fkSdoMZi+Ms="
|
||||||
{
|
},
|
||||||
"type": "message_create",
|
"params" : {
|
||||||
"id": "1062015437679050760",
|
"for_user_id": "123",
|
||||||
"created_timestamp": "1542039186292",
|
"direct_message_events": [
|
||||||
"message_create": {
|
{
|
||||||
"target": {
|
"type": "message_create",
|
||||||
"recipient_id": "456"
|
"id": "1062015437679050760",
|
||||||
},
|
"created_timestamp": "1542039186292",
|
||||||
"sender_id": "123",
|
"message_create": {
|
||||||
"source_app_id": "268278",
|
"target": {
|
||||||
"message_data": {
|
"recipient_id": "456"
|
||||||
"text": "Hey! Hello!",
|
},
|
||||||
"entities": {
|
"sender_id": "123",
|
||||||
"hashtags": [],
|
"source_app_id": "268278",
|
||||||
"symbols": [],
|
"message_data": {
|
||||||
"user_mentions": [],
|
"text": "Hey! Hello!",
|
||||||
"urls": []
|
"entities": {
|
||||||
|
"hashtags": [],
|
||||||
|
"symbols": [],
|
||||||
|
"user_mentions": [],
|
||||||
|
"urls": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
"apps": {
|
||||||
"apps": {
|
"268278": {
|
||||||
"268278": {
|
"id": "268278",
|
||||||
"id": "268278",
|
"name": "Twitter Web Client",
|
||||||
"name": "Twitter Web Client",
|
"url": "http://twitter.com"
|
||||||
"url": "http://twitter.com"
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"123": {
|
|
||||||
"id": "123",
|
|
||||||
"created_timestamp": "1476091912921",
|
|
||||||
"name": "Zammad HQ",
|
|
||||||
"screen_name": "zammadhq",
|
|
||||||
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
|
||||||
"url": "https://t.co/XITyrXmhTP",
|
|
||||||
"protected": false,
|
|
||||||
"verified": false,
|
|
||||||
"followers_count": 426,
|
|
||||||
"friends_count": 509,
|
|
||||||
"statuses_count": 436,
|
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
|
||||||
},
|
},
|
||||||
"456": {
|
"users": {
|
||||||
"id": "456",
|
"123": {
|
||||||
"created_timestamp": "1290730789000",
|
"id": "123",
|
||||||
"name": "Martin Edenhofer",
|
"created_timestamp": "1476091912921",
|
||||||
"screen_name": "medenhofer",
|
"name": "Zammad HQ",
|
||||||
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
"screen_name": "zammadhq",
|
||||||
"url": "https://t.co/whm4HTWdMw",
|
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
||||||
"protected": false,
|
"url": "https://t.co/XITyrXmhTP",
|
||||||
"verified": false,
|
"protected": false,
|
||||||
"followers_count": 312,
|
"verified": false,
|
||||||
"friends_count": 314,
|
"followers_count": 426,
|
||||||
"statuses_count": 222,
|
"friends_count": 509,
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
"statuses_count": 436,
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
||||||
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
||||||
|
},
|
||||||
|
"456": {
|
||||||
|
"id": "456",
|
||||||
|
"created_timestamp": "1290730789000",
|
||||||
|
"name": "Martin Edenhofer",
|
||||||
|
"screen_name": "medenhofer",
|
||||||
|
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
||||||
|
"url": "https://t.co/whm4HTWdMw",
|
||||||
|
"protected": false,
|
||||||
|
"verified": false,
|
||||||
|
"followers_count": 312,
|
||||||
|
"friends_count": 314,
|
||||||
|
"statuses_count": 222,
|
||||||
|
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
||||||
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,162 +1,167 @@
|
||||||
{
|
{
|
||||||
"for_user_id": "123",
|
"headers": {
|
||||||
"tweet_create_events": [
|
"x-twitter-webhooks-signature": "sha256=DmARpz6wdgte6Vj+ePeqC+RHvEDokmwOIIqr4//utkk="
|
||||||
{
|
},
|
||||||
"created_at": "Thu Nov 15 23:31:30 +0000 2018",
|
"params": {
|
||||||
"id": 1063212927510081536,
|
"for_user_id": "123",
|
||||||
"id_str": "1063212927510081536",
|
"tweet_create_events": [
|
||||||
"text": "Hey @medenhofer ! #hello1234 https://t.co/f1kffFlwpN",
|
{
|
||||||
"display_text_range": [0, 29],
|
"created_at": "Thu Nov 15 23:31:30 +0000 2018",
|
||||||
"source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
|
"id": 1063212927510081536,
|
||||||
"truncated": false,
|
"id_str": "1063212927510081536",
|
||||||
"in_reply_to_status_id": null,
|
"text": "Hey @medenhofer ! #hello1234 https://t.co/f1kffFlwpN",
|
||||||
"in_reply_to_status_id_str": null,
|
"display_text_range": [0, 29],
|
||||||
"in_reply_to_user_id": null,
|
"source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
|
||||||
"in_reply_to_user_id_str": null,
|
"truncated": false,
|
||||||
"in_reply_to_screen_name": null,
|
"in_reply_to_status_id": null,
|
||||||
"user": {
|
"in_reply_to_status_id_str": null,
|
||||||
"id": 123,
|
"in_reply_to_user_id": null,
|
||||||
"id_str": "123",
|
"in_reply_to_user_id_str": null,
|
||||||
"name": "Zammad HQ",
|
"in_reply_to_screen_name": null,
|
||||||
"screen_name": "zammadhq",
|
"user": {
|
||||||
"location": null,
|
"id": 123,
|
||||||
"url": "http://zammad.com",
|
"id_str": "123",
|
||||||
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
"name": "Zammad HQ",
|
||||||
"translator_type": "none",
|
"screen_name": "zammadhq",
|
||||||
"protected": false,
|
"location": null,
|
||||||
"verified": false,
|
"url": "http://zammad.com",
|
||||||
"followers_count": 427,
|
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
||||||
"friends_count": 512,
|
"translator_type": "none",
|
||||||
"listed_count": 20,
|
"protected": false,
|
||||||
"favourites_count": 280,
|
"verified": false,
|
||||||
"statuses_count": 438,
|
"followers_count": 427,
|
||||||
"created_at": "Mon Oct 10 09:31:52 +0000 2016",
|
"friends_count": 512,
|
||||||
"utc_offset": null,
|
"listed_count": 20,
|
||||||
"time_zone": null,
|
"favourites_count": 280,
|
||||||
"geo_enabled": false,
|
"statuses_count": 438,
|
||||||
"lang": "en",
|
"created_at": "Mon Oct 10 09:31:52 +0000 2016",
|
||||||
"contributors_enabled": false,
|
"utc_offset": null,
|
||||||
"is_translator": false,
|
"time_zone": null,
|
||||||
"profile_background_color": "000000",
|
"geo_enabled": false,
|
||||||
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
|
"lang": "en",
|
||||||
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
|
"contributors_enabled": false,
|
||||||
"profile_background_tile": false,
|
"is_translator": false,
|
||||||
"profile_link_color": "31B068",
|
"profile_background_color": "000000",
|
||||||
"profile_sidebar_border_color": "000000",
|
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
|
||||||
"profile_sidebar_fill_color": "000000",
|
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
|
||||||
"profile_text_color": "000000",
|
"profile_background_tile": false,
|
||||||
"profile_use_background_image": false,
|
"profile_link_color": "31B068",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
"profile_sidebar_border_color": "000000",
|
||||||
"profile_banner_url": "https://pbs.twimg.com/profile_banners/123/1476097853",
|
"profile_sidebar_fill_color": "000000",
|
||||||
"default_profile": false,
|
"profile_text_color": "000000",
|
||||||
"default_profile_image": false,
|
"profile_use_background_image": false,
|
||||||
"following": null,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
||||||
"follow_request_sent": null,
|
"profile_banner_url": "https://pbs.twimg.com/profile_banners/123/1476097853",
|
||||||
"notifications": null
|
"default_profile": false,
|
||||||
},
|
"default_profile_image": false,
|
||||||
"geo": null,
|
"following": null,
|
||||||
"coordinates": null,
|
"follow_request_sent": null,
|
||||||
"place": null,
|
"notifications": null
|
||||||
"contributors": null,
|
},
|
||||||
"is_quote_status": false,
|
"geo": null,
|
||||||
"quote_count": 0,
|
"coordinates": null,
|
||||||
"reply_count": 0,
|
"place": null,
|
||||||
"retweet_count": 0,
|
"contributors": null,
|
||||||
"favorite_count": 0,
|
"is_quote_status": false,
|
||||||
"entities": {
|
"quote_count": 0,
|
||||||
"hashtags": [
|
"reply_count": 0,
|
||||||
{"text": "hello1234", "indices": [19, 29]}
|
"retweet_count": 0,
|
||||||
],
|
"favorite_count": 0,
|
||||||
"urls": [],
|
"entities": {
|
||||||
"user_mentions": [
|
"hashtags": [
|
||||||
{
|
{"text": "hello1234", "indices": [19, 29]}
|
||||||
"screen_name": "medenhofer",
|
],
|
||||||
"name": "Martin Edenhofer",
|
"urls": [],
|
||||||
"id": 456,
|
"user_mentions": [
|
||||||
"id_str": "456",
|
{
|
||||||
"indices": [4, 15]
|
"screen_name": "medenhofer",
|
||||||
}
|
"name": "Martin Edenhofer",
|
||||||
],
|
"id": 456,
|
||||||
"symbols": [],
|
"id_str": "456",
|
||||||
"media": [
|
"indices": [4, 15]
|
||||||
{
|
}
|
||||||
"id": 1063212885961248768,
|
],
|
||||||
"id_str": "1063212885961248768",
|
"symbols": [],
|
||||||
"indices": [30, 53],
|
"media": [
|
||||||
"media_url": "http://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
{
|
||||||
"media_url_https": "https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
"id": 1063212885961248768,
|
||||||
"url": "https://t.co/f1kffFlwpN",
|
"id_str": "1063212885961248768",
|
||||||
"display_url": "pic.twitter.com/f1kffFlwpN",
|
"indices": [30, 53],
|
||||||
"expanded_url": "https://twitter.com/zammadhq/status/1063212927510081536/photo/1",
|
"media_url": "http://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
||||||
"type": "photo",
|
"media_url_https": "https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
||||||
"sizes": {
|
"url": "https://t.co/f1kffFlwpN",
|
||||||
"thumb": {
|
"display_url": "pic.twitter.com/f1kffFlwpN",
|
||||||
"w": 150,
|
"expanded_url": "https://twitter.com/zammadhq/status/1063212927510081536/photo/1",
|
||||||
"h": 150,
|
"type": "photo",
|
||||||
"resize": "crop"
|
"sizes": {
|
||||||
},
|
"thumb": {
|
||||||
"large": {
|
"w": 150,
|
||||||
"w": 852,
|
"h": 150,
|
||||||
"h": 462,
|
"resize": "crop"
|
||||||
"resize": "fit"
|
},
|
||||||
},
|
"large": {
|
||||||
"medium": {
|
"w": 852,
|
||||||
"w": 852,
|
"h": 462,
|
||||||
"h": 462,
|
"resize": "fit"
|
||||||
"resize": "fit"
|
},
|
||||||
},
|
"medium": {
|
||||||
"small": {
|
"w": 852,
|
||||||
"w": 680,
|
"h": 462,
|
||||||
"h": 369,
|
"resize": "fit"
|
||||||
"resize": "fit"
|
},
|
||||||
|
"small": {
|
||||||
|
"w": 680,
|
||||||
|
"h": 369,
|
||||||
|
"resize": "fit"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
},
|
||||||
},
|
"extended_entities": {
|
||||||
"extended_entities": {
|
"media": [
|
||||||
"media": [
|
{
|
||||||
{
|
"id": 1063212885961248768,
|
||||||
"id": 1063212885961248768,
|
"id_str": "1063212885961248768",
|
||||||
"id_str": "1063212885961248768",
|
"indices": [30, 53],
|
||||||
"indices": [30, 53],
|
"media_url": "http://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
||||||
"media_url": "http://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
"media_url_https": "https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
||||||
"media_url_https": "https://pbs.twimg.com/media/DsFKfJRWkAAFEbo.jpg",
|
"url": "https://t.co/f1kffFlwpN",
|
||||||
"url": "https://t.co/f1kffFlwpN",
|
"display_url": "pic.twitter.com/f1kffFlwpN",
|
||||||
"display_url": "pic.twitter.com/f1kffFlwpN",
|
"expanded_url": "https://twitter.com/zammadhq/status/1063212927510081536/photo/1",
|
||||||
"expanded_url": "https://twitter.com/zammadhq/status/1063212927510081536/photo/1",
|
"type": "photo",
|
||||||
"type": "photo",
|
"sizes": {
|
||||||
"sizes": {
|
"thumb": {
|
||||||
"thumb": {
|
"w": 150,
|
||||||
"w": 150,
|
"h": 150,
|
||||||
"h": 150,
|
"resize": "crop"
|
||||||
"resize": "crop"
|
},
|
||||||
},
|
"large": {
|
||||||
"large": {
|
"w": 852,
|
||||||
"w": 852,
|
"h": 462,
|
||||||
"h": 462,
|
"resize": "fit"
|
||||||
"resize": "fit"
|
},
|
||||||
},
|
"medium": {
|
||||||
"medium": {
|
"w": 852,
|
||||||
"w": 852,
|
"h": 462,
|
||||||
"h": 462,
|
"resize": "fit"
|
||||||
"resize": "fit"
|
},
|
||||||
},
|
"small": {
|
||||||
"small": {
|
"w": 680,
|
||||||
"w": 680,
|
"h": 369,
|
||||||
"h": 369,
|
"resize": "fit"
|
||||||
"resize": "fit"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
},
|
||||||
},
|
"favorited": false,
|
||||||
"favorited": false,
|
"retweeted": false,
|
||||||
"retweeted": false,
|
"possibly_sensitive": false,
|
||||||
"possibly_sensitive": false,
|
"filter_level": "low",
|
||||||
"filter_level": "low",
|
"lang": "und",
|
||||||
"lang": "und",
|
"timestamp_ms": "1542324690116"
|
||||||
"timestamp_ms": "1542324690116"
|
}
|
||||||
}
|
]
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,113 +1,118 @@
|
||||||
{
|
{
|
||||||
"for_user_id": "123",
|
"headers" : {
|
||||||
"direct_message_events": [
|
"x-twitter-webhooks-signature" : "sha256=wYiCk7gfAgrnerCpj3XD58hozfVDjcQvcYPZCFH+stU="
|
||||||
{
|
},
|
||||||
"type": "message_create",
|
"params": {
|
||||||
"id": "1063077238797725700",
|
"for_user_id": "123",
|
||||||
"created_timestamp": "1542292339406",
|
"direct_message_events": [
|
||||||
"message_create": {
|
{
|
||||||
"target": {
|
"type": "message_create",
|
||||||
"recipient_id": "123"
|
"id": "1063077238797725700",
|
||||||
},
|
"created_timestamp": "1542292339406",
|
||||||
"sender_id": "456",
|
"message_create": {
|
||||||
"message_data": {
|
"target": {
|
||||||
"text": "Hello Zammad #zammad @znuny\n\nYeah! https://t.co/UfaCwi9cUb",
|
"recipient_id": "123"
|
||||||
"entities": {
|
|
||||||
"hashtags": [
|
|
||||||
{
|
|
||||||
"text": "zammad",
|
|
||||||
"indices": [13,20]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"symbols": [],
|
|
||||||
"user_mentions": [
|
|
||||||
{
|
|
||||||
"screen_name": "znuny",
|
|
||||||
"name": "Znuny / ES for OTRS",
|
|
||||||
"id": 789,
|
|
||||||
"id_str": "789",
|
|
||||||
"indices": [21, 27]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"urls": [
|
|
||||||
{
|
|
||||||
"url": "https://t.co/UfaCwi9cUb",
|
|
||||||
"expanded_url": "https://twitter.com/messages/media/1063077238797725700",
|
|
||||||
"display_url": "pic.twitter.com/UfaCwi9cUb",
|
|
||||||
"indices": [35, 58]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"attachment": {
|
"sender_id": "456",
|
||||||
"type": "media",
|
"message_data": {
|
||||||
"media": {
|
"text": "Hello Zammad #zammad @znuny\n\nYeah! https://t.co/UfaCwi9cUb",
|
||||||
"id": 1063077198536556545,
|
"entities": {
|
||||||
"id_str": "1063077198536556545",
|
"hashtags": [
|
||||||
"indices": [35, 58],
|
{
|
||||||
"media_url": "https://ton.twitter.com/1.1/ton/data/dm/1063077238797725700/1063077198536556545/9FZgsMdV.jpg",
|
"text": "zammad",
|
||||||
"media_url_https": "https://ton.twitter.com/1.1/ton/data/dm/1063077238797725700/1063077198536556545/9FZgsMdV.jpg",
|
"indices": [13,20]
|
||||||
"url": "https://t.co/UfaCwi9cUb",
|
}
|
||||||
"display_url": "pic.twitter.com/UfaCwi9cUb",
|
],
|
||||||
"expanded_url": "https://twitter.com/messages/media/1063077238797725700",
|
"symbols": [],
|
||||||
"type": "photo",
|
"user_mentions": [
|
||||||
"sizes": {
|
{
|
||||||
"thumb": {
|
"screen_name": "znuny",
|
||||||
"w": 150,
|
"name": "Znuny / ES for OTRS",
|
||||||
"h": 150,
|
"id": 789,
|
||||||
"resize": "crop"
|
"id_str": "789",
|
||||||
},
|
"indices": [21, 27]
|
||||||
"medium": {
|
}
|
||||||
"w": 1200,
|
],
|
||||||
"h": 313,
|
"urls": [
|
||||||
"resize": "fit"
|
{
|
||||||
},
|
"url": "https://t.co/UfaCwi9cUb",
|
||||||
"small": {
|
"expanded_url": "https://twitter.com/messages/media/1063077238797725700",
|
||||||
"w": 680,
|
"display_url": "pic.twitter.com/UfaCwi9cUb",
|
||||||
"h": 177,
|
"indices": [35, 58]
|
||||||
"resize": "fit"
|
}
|
||||||
},
|
]
|
||||||
"large": {
|
},
|
||||||
"w": 1472,
|
"attachment": {
|
||||||
"h": 384,
|
"type": "media",
|
||||||
"resize": "fit"
|
"media": {
|
||||||
|
"id": 1063077198536556545,
|
||||||
|
"id_str": "1063077198536556545",
|
||||||
|
"indices": [35, 58],
|
||||||
|
"media_url": "https://ton.twitter.com/1.1/ton/data/dm/1063077238797725700/1063077198536556545/9FZgsMdV.jpg",
|
||||||
|
"media_url_https": "https://ton.twitter.com/1.1/ton/data/dm/1063077238797725700/1063077198536556545/9FZgsMdV.jpg",
|
||||||
|
"url": "https://t.co/UfaCwi9cUb",
|
||||||
|
"display_url": "pic.twitter.com/UfaCwi9cUb",
|
||||||
|
"expanded_url": "https://twitter.com/messages/media/1063077238797725700",
|
||||||
|
"type": "photo",
|
||||||
|
"sizes": {
|
||||||
|
"thumb": {
|
||||||
|
"w": 150,
|
||||||
|
"h": 150,
|
||||||
|
"resize": "crop"
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"w": 1200,
|
||||||
|
"h": 313,
|
||||||
|
"resize": "fit"
|
||||||
|
},
|
||||||
|
"small": {
|
||||||
|
"w": 680,
|
||||||
|
"h": 177,
|
||||||
|
"resize": "fit"
|
||||||
|
},
|
||||||
|
"large": {
|
||||||
|
"w": 1472,
|
||||||
|
"h": 384,
|
||||||
|
"resize": "fit"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
"users": {
|
||||||
"users": {
|
"456": {
|
||||||
"456": {
|
"id": "456",
|
||||||
"id": "456",
|
"created_timestamp": "1290730789000",
|
||||||
"created_timestamp": "1290730789000",
|
"name": "Martin Edenhofer",
|
||||||
"name": "Martin Edenhofer",
|
"screen_name": "medenhofer",
|
||||||
"screen_name": "medenhofer",
|
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
||||||
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
"url": "https://t.co/whm4HTWdMw",
|
||||||
"url": "https://t.co/whm4HTWdMw",
|
"protected": false,
|
||||||
"protected": false,
|
"verified": false,
|
||||||
"verified": false,
|
"followers_count": 312,
|
||||||
"followers_count": 312,
|
"friends_count": 314,
|
||||||
"friends_count": 314,
|
"statuses_count": 222,
|
||||||
"statuses_count": 222,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
},
|
||||||
},
|
"123": {
|
||||||
"123": {
|
"id": "123",
|
||||||
"id": "123",
|
"created_timestamp": "1476091912921",
|
||||||
"created_timestamp": "1476091912921",
|
"name": "Zammad HQ",
|
||||||
"name": "Zammad HQ",
|
"screen_name": "zammadhq",
|
||||||
"screen_name": "zammadhq",
|
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
||||||
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
"url": "https://t.co/XITyrXmhTP",
|
||||||
"url": "https://t.co/XITyrXmhTP",
|
"protected": false,
|
||||||
"protected": false,
|
"verified": false,
|
||||||
"verified": false,
|
"followers_count": 427,
|
||||||
"followers_count": 427,
|
"friends_count": 512,
|
||||||
"friends_count": 512,
|
"statuses_count": 437,
|
||||||
"statuses_count": 437,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,98 @@
|
||||||
{
|
{
|
||||||
"for_user_id": "123",
|
"headers": {
|
||||||
"tweet_create_events": [
|
"x-twitter-webhooks-signature": "sha256=U7bglX19JitI2xuvyONAc0d/fowIFEeUzkEgnWdGyUM="
|
||||||
{
|
},
|
||||||
"created_at": "Wed Nov 21 00:13:13 +0000 2018",
|
"params": {
|
||||||
"id": 1065035365336141825,
|
"for_user_id": "123",
|
||||||
"id_str": "1065035365336141825",
|
"tweet_create_events": [
|
||||||
"text": "@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et… https://t.co/b9woj0QXNZ",
|
{
|
||||||
"source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
|
"created_at": "Wed Nov 21 00:13:13 +0000 2018",
|
||||||
"truncated": true,
|
"id": 1065035365336141825,
|
||||||
"in_reply_to_status_id": null,
|
"id_str": "1065035365336141825",
|
||||||
"in_reply_to_status_id_str": null,
|
"text": "@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et… https://t.co/b9woj0QXNZ",
|
||||||
"in_reply_to_user_id": 123,
|
"source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
|
||||||
"in_reply_to_user_id_str": "123",
|
"truncated": true,
|
||||||
"in_reply_to_screen_name": "znuny",
|
"in_reply_to_status_id": null,
|
||||||
"user": {
|
"in_reply_to_status_id_str": null,
|
||||||
"id": 219826253,
|
"in_reply_to_user_id": 123,
|
||||||
"id_str": "219826253",
|
"in_reply_to_user_id_str": "123",
|
||||||
"name": "Martin Edenhofer",
|
"in_reply_to_screen_name": "znuny",
|
||||||
"screen_name": "medenhofer",
|
"user": {
|
||||||
"location": null,
|
"id": 219826253,
|
||||||
"url": "http://edenhofer.de/",
|
"id_str": "219826253",
|
||||||
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
"name": "Martin Edenhofer",
|
||||||
"translator_type": "regular",
|
"screen_name": "medenhofer",
|
||||||
"protected": false,
|
"location": null,
|
||||||
"verified": false,
|
"url": "http://edenhofer.de/",
|
||||||
"followers_count": 310,
|
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
||||||
"friends_count": 314,
|
"translator_type": "regular",
|
||||||
"listed_count": 16,
|
"protected": false,
|
||||||
"favourites_count": 129,
|
"verified": false,
|
||||||
"statuses_count": 225,
|
"followers_count": 310,
|
||||||
"created_at": "Fri Nov 26 00:19:49 +0000 2010",
|
"friends_count": 314,
|
||||||
"utc_offset": null,
|
"listed_count": 16,
|
||||||
"time_zone": null,
|
"favourites_count": 129,
|
||||||
"geo_enabled": false,
|
"statuses_count": 225,
|
||||||
"lang": "en",
|
"created_at": "Fri Nov 26 00:19:49 +0000 2010",
|
||||||
"contributors_enabled": false,
|
"utc_offset": null,
|
||||||
"is_translator": false,
|
"time_zone": null,
|
||||||
"profile_background_color": "C0DEED",
|
"geo_enabled": false,
|
||||||
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
|
"lang": "en",
|
||||||
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
|
"contributors_enabled": false,
|
||||||
"profile_background_tile": true, "profile_link_color": "0084B4", "profile_sidebar_border_color": "FFFFFF",
|
"is_translator": false,
|
||||||
"profile_sidebar_fill_color": "DDEEF6",
|
"profile_background_color": "C0DEED",
|
||||||
"profile_text_color": "333333",
|
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
|
||||||
"profile_use_background_image": true,
|
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
"profile_background_tile": true, "profile_link_color": "0084B4", "profile_sidebar_border_color": "FFFFFF",
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
"profile_sidebar_fill_color": "DDEEF6",
|
||||||
"profile_banner_url": "https://pbs.twimg.com/profile_banners/219826253/1349428277",
|
"profile_text_color": "333333",
|
||||||
"default_profile": false,
|
"profile_use_background_image": true,
|
||||||
"default_profile_image": false,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
||||||
"following": null,
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
||||||
"follow_request_sent": null,
|
"profile_banner_url": "https://pbs.twimg.com/profile_banners/219826253/1349428277",
|
||||||
"notifications": null
|
"default_profile": false,
|
||||||
},
|
"default_profile_image": false,
|
||||||
"geo": null,
|
"following": null,
|
||||||
"coordinates": null,
|
"follow_request_sent": null,
|
||||||
"place": null,
|
"notifications": null
|
||||||
"contributors": null,
|
},
|
||||||
"is_quote_status": false,
|
"geo": null,
|
||||||
"extended_tweet": {
|
"coordinates": null,
|
||||||
"full_text": "@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lore",
|
"place": null,
|
||||||
"display_text_range": [0, 279],
|
"contributors": null,
|
||||||
|
"is_quote_status": false,
|
||||||
|
"extended_tweet": {
|
||||||
|
"full_text": "@znuny Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lore",
|
||||||
|
"display_text_range": [0, 279],
|
||||||
|
"entities": {
|
||||||
|
"hashtags": [],
|
||||||
|
"urls": [],
|
||||||
|
"user_mentions": [
|
||||||
|
{
|
||||||
|
"screen_name": "znuny",
|
||||||
|
"name": "Znuny / ES for OTRS",
|
||||||
|
"id": 123,
|
||||||
|
"id_str": "123",
|
||||||
|
"indices": [0, 6]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"symbols": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"quote_count": 0,
|
||||||
|
"reply_count": 0,
|
||||||
|
"retweet_count": 0,
|
||||||
|
"favorite_count": 0,
|
||||||
"entities": {
|
"entities": {
|
||||||
"hashtags": [],
|
"hashtags": [],
|
||||||
"urls": [],
|
"urls": [
|
||||||
|
{
|
||||||
|
"url": "https://t.co/b9woj0QXNZ",
|
||||||
|
"expanded_url": "https://twitter.com/i/web/status/1065035365336141825",
|
||||||
|
"display_url": "twitter.com/i/web/status/1…",
|
||||||
|
"indices": [117, 140]
|
||||||
|
}
|
||||||
|
],
|
||||||
"user_mentions": [
|
"user_mentions": [
|
||||||
{
|
{
|
||||||
"screen_name": "znuny",
|
"screen_name": "znuny",
|
||||||
|
@ -73,38 +103,13 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"symbols": []
|
"symbols": []
|
||||||
}
|
},
|
||||||
},
|
"favorited": false,
|
||||||
"quote_count": 0,
|
"retweeted": false,
|
||||||
"reply_count": 0,
|
"filter_level": "low",
|
||||||
"retweet_count": 0,
|
"lang": "ro",
|
||||||
"favorite_count": 0,
|
"timestamp_ms": "1542759193153"
|
||||||
"entities": {
|
}
|
||||||
"hashtags": [],
|
]
|
||||||
"urls": [
|
}
|
||||||
{
|
}
|
||||||
"url": "https://t.co/b9woj0QXNZ",
|
|
||||||
"expanded_url": "https://twitter.com/i/web/status/1065035365336141825",
|
|
||||||
"display_url": "twitter.com/i/web/status/1…",
|
|
||||||
"indices": [117, 140]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"user_mentions": [
|
|
||||||
{
|
|
||||||
"screen_name": "znuny",
|
|
||||||
"name": "Znuny / ES for OTRS",
|
|
||||||
"id": 123,
|
|
||||||
"id_str": "123",
|
|
||||||
"indices": [0, 6]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"symbols": []
|
|
||||||
},
|
|
||||||
"favorited": false,
|
|
||||||
"retweeted": false,
|
|
||||||
"filter_level": "low",
|
|
||||||
"lang": "ro",
|
|
||||||
"timestamp_ms": "1542759193153"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,57 +1,62 @@
|
||||||
{
|
{
|
||||||
"for_user_id": "123",
|
"headers" : {
|
||||||
"direct_message_events": [
|
"x-twitter-webhooks-signature" : "sha256=OTguUdchBdxNal/csZsRkytKL5srrUuezZ3hp/2E404="
|
||||||
{
|
},
|
||||||
"type": "message_create",
|
"params" : {
|
||||||
"id": "1063077238797725701",
|
"for_user_id": "123",
|
||||||
"created_timestamp": "1542292339406",
|
"direct_message_events": [
|
||||||
"message_create": {
|
{
|
||||||
"target": {
|
"type": "message_create",
|
||||||
"recipient_id": "123"
|
"id": "1063077238797725701",
|
||||||
},
|
"created_timestamp": "1542292339406",
|
||||||
"sender_id": "456",
|
"message_create": {
|
||||||
"message_data": {
|
"target": {
|
||||||
"text": "Hello again!",
|
"recipient_id": "123"
|
||||||
"entities": {
|
},
|
||||||
"hashtags": [],
|
"sender_id": "456",
|
||||||
"symbols": [],
|
"message_data": {
|
||||||
"user_mentions": [],
|
"text": "Hello again!",
|
||||||
"urls": []
|
"entities": {
|
||||||
|
"hashtags": [],
|
||||||
|
"symbols": [],
|
||||||
|
"user_mentions": [],
|
||||||
|
"urls": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
"users": {
|
||||||
"users": {
|
"456": {
|
||||||
"456": {
|
"id": "456",
|
||||||
"id": "456",
|
"created_timestamp": "1290730789000",
|
||||||
"created_timestamp": "1290730789000",
|
"name": "Martin Edenhofer",
|
||||||
"name": "Martin Edenhofer",
|
"screen_name": "medenhofer",
|
||||||
"screen_name": "medenhofer",
|
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
||||||
"description": "Open Source professional and geek. Also known as #OTRS and #Zammad inventor. ;)\r\nEntrepreneur and Advisor for open source people in need.",
|
"url": "https://t.co/whm4HTWdMw",
|
||||||
"url": "https://t.co/whm4HTWdMw",
|
"protected": false,
|
||||||
"protected": false,
|
"verified": false,
|
||||||
"verified": false,
|
"followers_count": 312,
|
||||||
"followers_count": 312,
|
"friends_count": 314,
|
||||||
"friends_count": 314,
|
"statuses_count": 222,
|
||||||
"statuses_count": 222,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg",
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/794220000450150401/D-eFg44R_normal.jpg"
|
},
|
||||||
},
|
"123": {
|
||||||
"123": {
|
"id": "123",
|
||||||
"id": "123",
|
"created_timestamp": "1476091912921",
|
||||||
"created_timestamp": "1476091912921",
|
"name": "Zammad HQ",
|
||||||
"name": "Zammad HQ",
|
"screen_name": "zammadhq",
|
||||||
"screen_name": "zammadhq",
|
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
||||||
"description": "Helpdesk and Customer Support made easy. Open Source for download or to go with SaaS. #zammad",
|
"url": "https://t.co/XITyrXmhTP",
|
||||||
"url": "https://t.co/XITyrXmhTP",
|
"protected": false,
|
||||||
"protected": false,
|
"verified": false,
|
||||||
"verified": false,
|
"followers_count": 427,
|
||||||
"followers_count": 427,
|
"friends_count": 512,
|
||||||
"friends_count": 512,
|
"statuses_count": 437,
|
||||||
"statuses_count": 437,
|
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
||||||
"profile_image_url": "http://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg",
|
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
||||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/785412960797745152/wxdIvejo_normal.jpg"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue