yammdesk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Yammdesk
2
+
3
+
4
+ ## WARNING
5
+ WARNING: THIS SOFTWARE IS ALPHA. It has bugs which may set fire to your wife. If you do not have a wife, it may seek out the equivalent and set fire to that. I strongly suggest you do not use it until this notice disappears.
6
+
7
+ ## Description
8
+
9
+ A two-way interface between Yammer, and your ticketing system. Currently only Web Help Desk is supported, but there are plans to include Zendesk.
10
+
11
+ ## Installation
12
+
13
+ $ gem install yammdesk
14
+
15
+ Use the configuration template provided in the templates folder to create a conf file in the folder you will be running the script from.
16
+
17
+ ## Usage
18
+
19
+ Run SyncTicketing.rb to sync Yammer <=> Ticketing System.
20
+
21
+ Alternatively, you can use the methods provided to write your own solution.
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO ADDED
@@ -0,0 +1,8 @@
1
+ # TODO
2
+
3
+ Many things are broken ;)
4
+
5
+ * Ensure that tokens are not passed via URL
6
+ * Refactor API call methods into one method that can be called for all jobs.
7
+ * Ensure regular expressions that re-write the API call responses catch _all_ abnormal text (HTML) and re-write them to plain text.
8
+ * Look into using ['rich'] Yammer message response instead of ['plain'], to get around the Yammer user name change bug (which means that if a Yammer user changes their name, any messages they send will not be found when searching the ticketing system).
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yammdesk'
4
+ require 'colorize'
5
+ require 'cgi'
6
+
7
+ beginning = Time.now
8
+ yamm = Yammdesk::Yammer.new
9
+ whd = Yammdesk::WHD.new
10
+ yamm_results = yamm.get
11
+ yamm_results.sort_by { |id| }
12
+
13
+
14
+ yamm_results.each { |m|
15
+ id = m['id']
16
+ replied_to_id = m["replied_to_id"]
17
+ thread_id = m["thread_id"]
18
+ body = m["body"]["plain"]
19
+
20
+ thread_exists = whd.thread_exists?(thread_id)
21
+ thread_starter = yamm.is_thread_starter?(id, thread_id)
22
+
23
+ # TODO: Write these methods
24
+ # ticket_closed = WHD.ticket_closed?(thread_id)
25
+ #
26
+ if thread_exists && !thread_starter
27
+ puts "Thread is not a thread starter, AND ticket exists".yellow
28
+ puts "thread_exists = #{thread_exists}\nthread_starter = #{thread_starter}".yellow
29
+ puts "thread_id = #{thread_id}".yellow
30
+ puts "messag id = #{id}".yellow
31
+ puts "body = #{body}\n".yellow
32
+
33
+ ticket_id = whd.search(thread_id)
34
+ if !whd.note_exists?(ticket_id, body)
35
+ puts "Thread is not a thread starter, AND ticket exists, AND note does not exist".blue
36
+ puts "thread_exists = #{thread_exists}\nthread_starter = #{thread_starter}".blue
37
+ puts "thread_id = #{thread_id}".blue
38
+ puts "messag id = #{id}".blue
39
+ puts "body = #{body}\n".blue
40
+
41
+ whd.update_ticket(ticket_id, body)
42
+ end
43
+ elsif !thread_exists && thread_starter
44
+ puts "Thread does not exist, AND is thread_starter".green
45
+ puts "thread_exists = #{thread_exists}\nthread_starter = #{thread_starter}".green
46
+ puts "thread_id = #{thread_id}".green
47
+ puts "messag id = #{id}".green
48
+ puts "body = #{body}".green
49
+
50
+ whd.create_ticket(thread_id, body)
51
+
52
+ ticket_id = whd.search(thread_id)
53
+ thread_replies = yamm.get_thread_replies(thread_id)
54
+ thread_replies.sort_by { |id| }
55
+ thread_replies.each { |m|
56
+ id = m['id']
57
+ replied_to_id = m["replied_to_id"]
58
+ thread_id = m["thread_id"]
59
+ body = m["body"]["plain"]
60
+
61
+ if !yamm.is_thread_starter?(id, thread_id)
62
+ whd.update_ticket(ticket_id, body)
63
+ end
64
+ }
65
+ elsif !thread_exists && !thread_starter
66
+ puts "Thread does not exist AND this is not a thread starter".red
67
+ puts "thread_exists = #{thread_exists}\nthread_starter = #{thread_starter}".red
68
+ puts "thread_id = #{thread_id}".red
69
+ puts "messag id = #{id}".red
70
+ puts "body = #{body}\n".red
71
+
72
+ missing_thread_starter = yamm.get_thread_starter(thread_id)
73
+
74
+ id = missing_thread_starter['id']
75
+ replied_to_id = missing_thread_starter["replied_to_id"]
76
+ thread_id = missing_thread_starter["thread_id"]
77
+ body = missing_thread_starter["body"]["plain"]
78
+
79
+ # We found a missing thread starter from a reply returned by the YAPI
80
+ # Let's create a ticket from the ID we found then:
81
+ whd.create_ticket(thread_id, body)
82
+
83
+ # ... and get all the replies
84
+ ticket_id = whd.search(thread_id)
85
+
86
+ thread_replies = yamm.get_thread_replies(thread_id)
87
+ thread_replies.sort_by { |id| }
88
+ thread_replies.each { |m|
89
+ id = m['id']
90
+ replied_to_id = m["replied_to_id"]
91
+ thread_id = m["thread_id"]
92
+ body = m["body"]["plain"]
93
+
94
+ if !yamm.is_thread_starter?(id, thread_id)
95
+ whd.update_ticket(ticket_id, body)
96
+ end
97
+ }
98
+ else
99
+ puts "Thread exists AND this is a thread starter\n".red
100
+ end
101
+ }
102
+
103
+
104
+ # Now check for updated tickets, and push to Yammer
105
+ whd_tickets = whd.get
106
+ whd_tickets.sort_by { |id| }
107
+
108
+ whd_tickets.each { |t|
109
+ yamm_id = t['shortSubject']
110
+ ticket_id = t['id']
111
+ whd_id = whd.search(yamm_id)
112
+ yamm_replies = yamm.get_thread_replies(yamm_id)
113
+ whd_notes = whd.get_notes(ticket_id)
114
+
115
+ whd_notes.each { | note |
116
+ puts "Now searching for note:\n#{note}\n".red
117
+
118
+ if yamm.reply_exists?(note, yamm_replies)
119
+ puts "Ticket: #{ticket_id}\n"
120
+ puts "Thread from WHD: #{yamm_id}\n"
121
+ puts "Nothin' doin'. The note is already a comment on Yammer:\n" + "#{note}\n\n".blue
122
+ else
123
+ puts "Ticket: #{ticket_id}\n"
124
+ puts "Thread from WHD: #{yamm_id}\n"
125
+ puts "Didn't find the note in the thread. Updating it with the new reply:\n" + "#{note}\n\n".green
126
+ yamm.update_thread(yamm_id, note)
127
+ end
128
+ }
129
+
130
+ }
131
+
132
+ puts "Application run completed in #{Time.now - beginning} seconds\n"
data/lib/yammdesk.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "yammdesk/version"
2
+
3
+ module Yammdesk
4
+ require_relative 'yammdesk/yammer'
5
+ require 'yaml'
6
+ require 'net/http'
7
+ require 'openssl'
8
+ require 'uri'
9
+ require 'json'
10
+ require 'nokogiri'
11
+
12
+
13
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
14
+ CONFIG = YAML.load_file("config.yml") unless defined? CONFIG
15
+ end
@@ -0,0 +1,3 @@
1
+ module Yammdesk
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,158 @@
1
+ module Yammdesk
2
+
3
+ class WHD
4
+
5
+ require 'yaml'
6
+ require 'net/http'
7
+ require 'openssl'
8
+ require 'uri'
9
+ require 'json'
10
+ require 'colorize'
11
+
12
+ CONFIG = YAML.load_file("config.yml") unless defined? CONFIG
13
+
14
+
15
+ def initialize
16
+ @url = CONFIG['whd']['url']
17
+ @token = CONFIG['whd']['tech_token']
18
+ @uri = URI(@url)
19
+ current_dir = File.dirname(__FILE__)
20
+ @post_file = JSON.parse(IO.read("#{current_dir}/../../templates/create_ticket.json"))
21
+ @update_file = JSON.parse(IO.read("#{current_dir}/../../templates/update_ticket.json"))
22
+ end
23
+
24
+ def get
25
+ Net::HTTP.start(@uri.host, @uri.port) do |http|
26
+ response = http.get(@url + "Tickets/?list=group&apiKey=#{@token}")
27
+ # response = http.get(@url + "Tickets/?list=group", :params => {:apiKey => @token})
28
+ @data = JSON.parse(response.body)
29
+ end
30
+ end
31
+
32
+ def thread_exists?(thread_id)
33
+ url = @url + "Tickets?qualifier=(subject%3D'#{thread_id}')&apiKey=#{@token}"
34
+ uri = URI(url)
35
+
36
+ Net::HTTP.start(uri.host, uri.port) do |http|
37
+ response = http.get(url)
38
+ get_data = JSON.parse(response.body)
39
+
40
+ if get_data.empty?
41
+ return false
42
+ else
43
+ return true
44
+ end
45
+ end
46
+ end
47
+
48
+ def note_exists?(ticket_id, yamm_body)
49
+ yamm_body = CGI.unescapeHTML(yamm_body)
50
+ url = @url + "TicketNotes?jobTicketId=#{ticket_id}&apiKey=#{@token}"
51
+ uri = URI(url)
52
+
53
+ Net::HTTP.start(@uri.host, @uri.port) do |http|
54
+ response = http.get(url)
55
+ get_data = JSON.parse(response.body)
56
+
57
+ if note = get_data.find { |note| CGI.unescapeHTML(note['mobileNoteText']) }
58
+ return true
59
+ else
60
+ return false
61
+ end
62
+ end
63
+ end
64
+
65
+ def get_notes(ticket_id)
66
+ url = @url + "TicketNotes?jobTicketId=#{ticket_id}&apiKey=#{@token}"
67
+ uri = URI(url)
68
+
69
+ Net::HTTP.start(@uri.host, @uri.port) do |http|
70
+ response = http.get(url)
71
+ get_data = JSON.parse(response.body)
72
+
73
+ @whd_entries = []
74
+
75
+ get_data.each { |h|
76
+ note = CGI.unescapeHTML(h['mobileNoteText'])
77
+ note = note.gsub('<br/> ', "\n")
78
+ note = Nokogiri::HTML.parse(note)
79
+ note = note.text
80
+
81
+ @whd_entries << note
82
+ }
83
+
84
+ return @whd_entries
85
+ end
86
+ end
87
+
88
+ def search(thread_id)
89
+ url = @url + "Tickets?qualifier=(subject%3D'#{thread_id}')&apiKey=#{@token}"
90
+ uri = URI(url)
91
+
92
+ Net::HTTP.start(uri.host, uri.port) do |http|
93
+ response = http.get(url)
94
+ #puts "DEBUG: #{response.code}, #{response.body}"
95
+
96
+ get_data = JSON.parse(response.body)
97
+ #data = (get_data[0] || {}).to_hash
98
+ data = get_data[0].to_hash
99
+ ticket_id = data["id"]
100
+
101
+ return ticket_id
102
+ end
103
+ end
104
+
105
+ def put(options = {})
106
+ ticket_id = options[:ticket_id]
107
+ put_data = options[:put_data]
108
+
109
+ Net::HTTP.start(@uri.host, @uri.port) do |http|
110
+ response = http.request_put(@url + "Tickets/#{ticket_id}/?apiKey=#{@token}", put_data)
111
+ @data = JSON.parse(response.body)
112
+ end
113
+ end
114
+
115
+ def last_ticket
116
+ last_ticket = "10559118"
117
+ end
118
+
119
+ def update_ticket(ticket_id, message)
120
+ @update_file["ticketId"] = ticket_id
121
+ @update_file["jobticket"]["id"] = ticket_id
122
+ @update_file["noteText"] = message
123
+ post_data = @update_file.to_s
124
+ url = @url + "TechNotes?apiKey=#{@token}"
125
+ uri = URI(url)
126
+
127
+ request = Net::HTTP::Post.new(uri.request_uri)
128
+ request.body = post_data
129
+
130
+ response = Net::HTTP.new(uri.host, uri.port).start { |http|
131
+ res = http.request(request)
132
+ # puts "CODE: #{res.code} with BODY: #{res.body}\n".white
133
+ }
134
+ end
135
+
136
+ def create_ticket(thread_id, body)
137
+ @post_file["subject"] = "#{thread_id}"
138
+ @post_file["detail"] = body
139
+ post_data = @post_file.to_s
140
+
141
+ url = @url + "Tickets?apiKey=#{@token}"
142
+ uri = URI(url)
143
+
144
+ request = Net::HTTP::Post.new(uri.request_uri)
145
+ request.body = post_data
146
+
147
+ response = Net::HTTP.new(uri.host, uri.port).start { |http| http.request(request) }
148
+
149
+ # DEBUG
150
+ # puts "Creating ticket for thread: ".yellow + "#{thread_id}\n" + "with message: ".yellow + "#{post_data}\n" +
151
+ # "using put URL: ".yellow + "#{url}\n\n"
152
+ # DEBUG
153
+
154
+ end
155
+
156
+ end
157
+ end
158
+
@@ -0,0 +1,97 @@
1
+ module Yammdesk
2
+ class Yammer
3
+
4
+ require 'yaml'
5
+ require 'net/http'
6
+ require 'openssl'
7
+ require 'uri'
8
+ require 'json'
9
+ require 'nokogiri'
10
+ require_relative 'whd'
11
+
12
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
13
+ CONFIG = YAML.load_file("config.yml") unless defined? CONFIG
14
+
15
+
16
+ def initialize
17
+ @url = CONFIG['yammdesk']['url']
18
+ @token = CONFIG['yammdesk']['token']
19
+ @uri = URI(@url)
20
+ end
21
+
22
+
23
+ def get
24
+ Net::HTTP.start(@uri.host, @uri.port, :use_ssl => @uri.scheme == 'https') do |http|
25
+ response = http.get(@url + "/messages/in_group/872.json?oaccess_token=#{@token}")
26
+ @data = JSON.parse(response.body)
27
+
28
+ return @data['messages']
29
+ end
30
+ end
31
+
32
+ def get_thread_replies(thread_id)
33
+ Net::HTTP.start(@uri.host, @uri.port, :use_ssl => @uri.scheme == 'https') do |http|
34
+ response = http.get(@url + "/messages/in_thread/#{thread_id}.json?oaccess_token=#{@token}")
35
+ @data = JSON.parse(response.body)
36
+
37
+ return @data['messages']
38
+ end
39
+ end
40
+
41
+ def get_thread_starter(thread_id)
42
+ url = @url + "/messages/#{thread_id}.json?oaccess_token=#{@token}"
43
+ uri = URI(@url)
44
+
45
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
46
+ response = http.get(url)
47
+ @data = JSON.parse(response.body)
48
+ return @data
49
+ end
50
+ end
51
+
52
+ def reply_exists?(whd_note, yamm_replies)
53
+ if yamm_replies.find { |yamm|
54
+ body = CGI.unescapeHTML(yamm['body']['plain'])
55
+ body = body.gsub('<br/> ', "\n")
56
+ body = Nokogiri::HTML.parse(body)
57
+ body = body.text
58
+
59
+ body == whd_note
60
+ }
61
+ return true
62
+ else
63
+ return false
64
+ end
65
+ end
66
+
67
+ def is_thread_starter?(id, thread_id)
68
+ if id == thread_id
69
+ true
70
+ else
71
+ false
72
+ end
73
+ end
74
+
75
+ def update_thread(thread_id, body)
76
+ body = CGI::unescapeHTML(body)
77
+ body = body.gsub('<br/> ', "\n")
78
+ body = Nokogiri::HTML.parse(body)
79
+ body = body.text
80
+
81
+ url = @url + "/messages.json&oaccess_token=#{@token}"
82
+ uri = URI(url)
83
+
84
+ https = Net::HTTP.new(uri.host, uri.port)
85
+ https.use_ssl = true
86
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
87
+
88
+ request = Net::HTTP::Post.new(uri.request_uri)
89
+ request.set_form_data('replied_to_id' => thread_id, 'body' => body)
90
+
91
+ response = https.request(request)
92
+ puts "DIDN'T FIND:\n#{body}\n\n".white
93
+ end
94
+
95
+
96
+ end
97
+ end