zulip 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +79 -0
  6. data/Rakefile +1 -0
  7. data/example/zulip_message_echo.rb +26 -0
  8. data/lib/zulip/client/event_parser.rb +14 -0
  9. data/lib/zulip/client/event_streaming.rb +86 -0
  10. data/lib/zulip/client/messages.rb +31 -0
  11. data/lib/zulip/client/queue_registration.rb +25 -0
  12. data/lib/zulip/client/stream_subscriptions.rb +40 -0
  13. data/lib/zulip/client/users.rb +16 -0
  14. data/lib/zulip/client.rb +53 -0
  15. data/lib/zulip/message.rb +18 -0
  16. data/lib/zulip/stream_subscription.rb +12 -0
  17. data/lib/zulip/user.rb +9 -0
  18. data/lib/zulip/version.rb +3 -0
  19. data/lib/zulip.rb +7 -0
  20. data/spec/fixtures/add-subscription.json +1 -0
  21. data/spec/fixtures/get-message-event-success.json +1 -0
  22. data/spec/fixtures/get-private-message.json +1 -0
  23. data/spec/fixtures/get-user-subscriptions.json +1 -0
  24. data/spec/fixtures/get-users.json +1 -0
  25. data/spec/fixtures/post-message-to-stream.json +1 -0
  26. data/spec/fixtures/queue-registration-success.json +1 -0
  27. data/spec/fixtures/sending-private-message-success.json +1 -0
  28. data/spec/helper.rb +13 -0
  29. data/spec/lib/zulip/client/event_parser_spec.rb +19 -0
  30. data/spec/lib/zulip/client/event_streaming_spec.rb +85 -0
  31. data/spec/lib/zulip/client/messages_spec.rb +64 -0
  32. data/spec/lib/zulip/client/queue_registration_spec.rb +44 -0
  33. data/spec/lib/zulip/client/stream_subscriptions_spec.rb +42 -0
  34. data/spec/lib/zulip/client/users_spec.rb +24 -0
  35. data/spec/lib/zulip/client_spec.rb +16 -0
  36. data/spec/lib/zulip/message_spec.rb +79 -0
  37. data/spec/lib/zulip/stream_subscription_spec.rb +21 -0
  38. data/spec/lib/zulip/user_spec.rb +17 -0
  39. data/zulip-rb.gemspec +27 -0
  40. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7104e27dedb5006e5b02e35bf0a8cadf8c74985b
4
+ data.tar.gz: a33826034e2546c0ef6957b1e41bde933ae5b342
5
+ SHA512:
6
+ metadata.gz: 4597dad59bb5b8269d3149a91c4629a6cf1f455ea961632b6a351cdcb39a1486e5d6a264609bcce36942b9910aea1b357729e5da9116a0c4e5ddca24929fdd40
7
+ data.tar.gz: ae289b999a7ebdd71d3504263de80d1255aba4f38aa4c3c141fa80ee28470774793a91e55bbb0bad6c961741cbe321f55725c2140c6302c1d823b20a247d0b5a
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ryan Vergeront
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Zulip-Rb
2
+
3
+ A ruby interface to the Zulip API.
4
+
5
+ ### Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ `gem 'zulip-rb'`
10
+
11
+ And then execute:
12
+
13
+ `$ bundle`
14
+
15
+ Or install it yourself as:
16
+
17
+ `$ gem install zulip-rb`
18
+
19
+ ### Configuration
20
+ You'll need to register a bot to use the Zulip API.
21
+ You can obtain your Zulip API key, create bots, and manage bots all
22
+ from your Zulip [settings page](https://zulip.com/#settings).
23
+
24
+ Set your api key and your bot's email address by passing a block to your Zulip::Client instance:
25
+ ```ruby
26
+ client = Zulip::Client.new do |config|
27
+ config.bot_email_address = "YOUR_BOTS_EMAIL_ADDRESS"
28
+ config.api_key = "YOUR_API_KEY"
29
+ end
30
+ ```
31
+
32
+ You'll need to subscribe your bot to streams you want to read from. Do so by adding your bot's email to the stream's membership from a zulip client, or use the #subscribe method (see example below).
33
+
34
+ ### Usage
35
+
36
+ Send messages to a stream:
37
+ ```ruby
38
+ client.send_message("Hey", "I'm posting to zulip", "test-stream")
39
+ ```
40
+
41
+ Send private messages to one or more users:
42
+ ```ruby
43
+ client.send_private_message("hey I heard you like the internet", "bob@the-internet.net", "alice@the-information-superhighway.org")
44
+ ```
45
+
46
+ Stream messages:
47
+ ```ruby
48
+ client.stream_messages do |message|
49
+ # Do some work
50
+ end
51
+ ```
52
+
53
+ Get your bot's current subscriptions:
54
+ ```ruby
55
+ client.get_subscriptions
56
+ ```
57
+
58
+ Subscribe your bot to a stream, passing the stream name:
59
+ ```ruby
60
+ client.subscribe "food"
61
+ ```
62
+
63
+ List users:
64
+ ```ruby
65
+ client.get_users
66
+ ```
67
+
68
+ ### Contributing
69
+
70
+ 1. Fork it
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create new Pull Request
75
+
76
+ Some areas that could use contributions:
77
+
78
+ 1. Error handling
79
+ 2. Add unsubscribing from a stream
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path('../../lib', __FILE__)
2
+ require 'zulip'
3
+
4
+ ### Sends a private message to a new thread is posted to a specified stream
5
+
6
+ class ZulipEcho
7
+
8
+ def run
9
+ client = Zulip::Client.new do |config|
10
+ config.bot_email_address = ENV['BOT_EMAIL_ADDRESS'] || "bot@example.com"
11
+ config.api_key = ENV['BOT_API_KEY'] || "apikey"
12
+ end
13
+
14
+ client.stream_messages do |message|
15
+ client.send_private_message(private_message_content(message), "example@gmail.com")
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def private_message_content(message)
22
+ "#{message.sender_full_name}, posted to #{message.stream}:\n #{message.subject}: #{message.content}"
23
+ end
24
+ end
25
+
26
+ ZulipEcho.new.run
@@ -0,0 +1,14 @@
1
+ module Zulip
2
+ class Client
3
+ module EventParser
4
+ def self.parse(event)
5
+ case event['type']
6
+ when "message"
7
+ Zulip::Message.new event.fetch('message')
8
+ when "heartbeat"
9
+ # NOOP
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,86 @@
1
+ module Zulip
2
+ class Client
3
+ module EventStreaming
4
+
5
+ def stream_messages(&block)
6
+ stream_events("message", &block)
7
+ end
8
+
9
+ def stream_private_messages(&block)
10
+ stream_raw_events("message") do |raw_event|
11
+ yield parse_event(raw_event) if private_message?(raw_event)
12
+ end
13
+ end
14
+
15
+ def stream_public_messages(&block)
16
+ stream_raw_events("message") do |raw_event|
17
+ yield parse_event(raw_event) if public_message?(raw_event)
18
+ end
19
+ end
20
+
21
+ def stream_events(event_types, &block)
22
+ stream_raw_events(event_types) do |raw_event|
23
+ yield parse_event(raw_event)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def stream_raw_events(event_types, &block)
30
+ queue = register(event_types)
31
+ queue_id = queue.queue_id
32
+ last_event_id = queue.last_event_id
33
+
34
+ loop do
35
+ events = get_events(queue_id, last_event_id)
36
+ last_event_id = max_event_id_from(events)
37
+
38
+ events.each do |event|
39
+ yield event if event_types.include?(event["type"])
40
+ end
41
+ end
42
+ end
43
+
44
+ # Makes a longpulling request on an event queue
45
+ def get_events(queue_id, last_event_id=nil)
46
+ connection.params = build_get_event_params(queue_id, last_event_id)
47
+ event_response.fetch("events")
48
+ end
49
+
50
+ def parse_event(raw_event)
51
+ Zulip::Client::EventParser.parse(raw_event)
52
+ end
53
+
54
+ def build_get_event_params(registered_queue_or_queue_id, last_event_id)
55
+ { 'queue_id' => find_queue_id(registered_queue_or_queue_id),
56
+ 'last_event_id' => last_event_id || registered_queue_or_queue_id.last_event_id }
57
+ end
58
+
59
+ def find_queue_id(registered_queue_or_queue_id)
60
+ if registered_queue_or_queue_id.respond_to?(:queue_id)
61
+ registered_queue_or_queue_id.queue_id
62
+ else
63
+ registered_queue_or_queue_id
64
+ end
65
+ end
66
+
67
+ def event_response
68
+ response = connection.get("/v1/events").body
69
+ JSON.parse(response)
70
+ end
71
+
72
+ def max_event_id_from(events)
73
+ events.map{ |event| event['id']}.max
74
+ end
75
+
76
+ def private_message?(event)
77
+ event['message']['type'] == "private"
78
+ end
79
+
80
+ def public_message?(event)
81
+ event['message']['type'] == "stream"
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,31 @@
1
+ require 'faraday'
2
+
3
+ module Zulip
4
+ class Client
5
+ module Messages
6
+
7
+ def send_message(subject, content, to_stream)
8
+ post_message("stream", content, to_stream, subject)
9
+ end
10
+
11
+ def send_private_message(content, *recipient_users)
12
+ post_message("private", content, recipient_users)
13
+ end
14
+
15
+ private
16
+
17
+ def post_message(type, content, recipients_or_stream, subject=nil)
18
+ connection.params = build_post_message_params(type, content,
19
+ recipients_or_stream, subject)
20
+ connection.post("v1/messages")
21
+ end
22
+
23
+ def build_post_message_params(type, content, recipients_or_stream, subject=nil)
24
+ params = subject ? {"subject" => subject } : {}
25
+ params.merge({ "type" => type, "content" => content,
26
+ "to" => json_encode_list(recipients_or_stream) })
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module Zulip
2
+ class Client
3
+ module QueueRegistration
4
+ EVENT_TYPES = { message: "message" }
5
+ # TODO: Add support for other event types
6
+ # TODO: Add support setting apply markdown to false
7
+
8
+ QueueRegistrationResponse = Struct.new(:queue_id, :last_event_id)
9
+
10
+ def register(event_types=nil, opts={})
11
+
12
+ if event_types
13
+ connection.params = { "event_types" => json_encode_list(event_types) }
14
+ end
15
+
16
+ QueueRegistrationResponse.new( registration_response['queue_id'],
17
+ registration_response['last_event_id'] )
18
+ end
19
+
20
+ def registration_response
21
+ @registration_response ||= parse_json(connection.post("v1/register").body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ module Zulip
2
+ class Client
3
+ module StreamSubscriptions
4
+
5
+ def get_subscriptions
6
+ get_subscriptions_response.map do |subscription|
7
+ Zulip::StreamSubscription.new(subscription)
8
+ end
9
+ end
10
+
11
+ def subscribe(streams)
12
+ patch_subscriptions subscribe_params(streams)
13
+ end
14
+
15
+ private
16
+
17
+ def get_subscriptions_response
18
+ parse_response(connection.get(subscription_urn)).fetch("subscriptions") { [] }
19
+ end
20
+
21
+ def subscription_urn
22
+ 'v1/users/me/subscriptions'
23
+ end
24
+
25
+ def subscribe_params(streams)
26
+ { add: json_encoded_streams(streams) }
27
+ end
28
+
29
+ def json_encoded_streams(streams)
30
+ streams = Array(streams).map { |subscription_name| { name: subscription_name } }
31
+ json_encode_list(streams)
32
+ end
33
+
34
+ def patch_subscriptions(req_params)
35
+ parse_response(connection.patch(subscription_urn, req_params))
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,16 @@
1
+ module Zulip
2
+ class Client
3
+ module Users
4
+ def get_users
5
+ get_users_response.map { |user_data| Zulip::User.new(user_data) }
6
+ end
7
+
8
+ private
9
+
10
+ def get_users_response
11
+ parse_response(connection.get('v1/users')).fetch("members") { [] }
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ require 'json'
2
+ require 'faraday'
3
+
4
+ require 'zulip/client/messages'
5
+ require 'zulip/client/users'
6
+ require 'zulip/client/stream_subscriptions'
7
+ require 'zulip/client/queue_registration'
8
+ require 'zulip/client/event_streaming'
9
+ require 'zulip/client/event_parser'
10
+
11
+ module Zulip
12
+ class Client
13
+ include Zulip::Client::Messages
14
+ include Zulip::Client::Users
15
+ include Zulip::Client::StreamSubscriptions
16
+ include Zulip::Client::QueueRegistration
17
+ include Zulip::Client::EventStreaming
18
+ include Zulip::Client::EventParser
19
+
20
+ attr_accessor :bot_email_address, :api_key
21
+ attr_writer :connection
22
+ ENDPOINT = "https://api.zulip.com"
23
+
24
+ def initialize
25
+ yield self if block_given?
26
+ end
27
+
28
+ def connection
29
+ @connection ||= initialize_connection
30
+ end
31
+
32
+ private
33
+
34
+ def initialize_connection
35
+ conn = Faraday.new(url: ENDPOINT)
36
+ conn.basic_auth(bot_email_address, api_key)
37
+ conn
38
+ end
39
+
40
+ def json_encode_list(items)
41
+ JSON.generate(Array(items).flatten)
42
+ end
43
+
44
+ def parse_response(http_response)
45
+ parse_json(http_response.body)
46
+ end
47
+
48
+ def parse_json(json)
49
+ JSON.parse(json)
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ module Zulip
2
+ class Message
3
+ attr_reader :id, :recipient_id, :sender_email, :timestamp, :display_recipient,
4
+ :sender_id, :sender_full_name, :sender_domain, :content, :gravatar_hash,
5
+ :avatar_url, :client, :content_type, :subject_links, :sender_short_name,
6
+ :type, :subject
7
+
8
+ alias :stream :display_recipient
9
+ alias :display_recipients :display_recipient
10
+
11
+ def initialize(attrs={})
12
+ attrs.each do |name, value|
13
+ instance_variable_set("@#{name}", value)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Zulip
2
+ class StreamSubscription
3
+ attr_reader :name, :color, :notifications, :subscribers, :invite_only,
4
+ :email_address, :in_home_view
5
+
6
+ def initialize(attrs={})
7
+ attrs.each do |name, value|
8
+ instance_variable_set("@#{name}", value)
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/zulip/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Zulip
2
+ class User
3
+ attr_reader :email, :full_name
4
+ def initialize(user_data)
5
+ @email = user_data['email']
6
+ @full_name = user_data['full_name']
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Zulip
2
+ VERSION = "0.0.1"
3
+ end
data/lib/zulip.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+
3
+ require 'zulip/version'
4
+ require 'zulip/client'
5
+ require 'zulip/message'
6
+ require 'zulip/user'
7
+ require 'zulip/stream_subscription'
@@ -0,0 +1 @@
1
+ {"subscribed":{"test_zulip_cli-bot@students.hackerschool.com":["clojure"]},"not_subscribed":[],"result":"success","msg":"","already_subscribed":{"test_zulip_cli-bot@students.hackerschool.com":["ruby"]},"removed":[]}
@@ -0,0 +1 @@
1
+ {"result":"success","queue_id":"1384457534:662","msg":"","events":[{"flags":[],"message":{"recipient_id":23442,"sender_email":"ryanvergeront@gmail.com","timestamp":1384460690,"display_recipient":"test-stream","sender_id":2874,"sender_full_name":"Ryan Vergeront (F'13)","sender_domain":"students.hackerschool.com","content":"register an event type of 'message' not 'messages'","gravatar_hash":"a8e2e659c929c79669f97603bd73fcc5","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/a8e2e659c929c79669f97603bd73fcc5?d=identicon","client":"desktop app Mac 0.3.10","content_type":"text\/x-markdown","subject_links":[],"sender_short_name":"ryanvergeront","type":"stream","id":12791579,"subject":"sending some internet websites to the internet"},"type":"message","id":0}]}
@@ -0,0 +1 @@
1
+ {"result":"success","queue_id":"1384457534:662","msg":"","events":[{"flags":[],"message":{"recipient_id":25207,"sender_email":"ryanvergeront@gmail.com","timestamp":1384561238,"display_recipient":[{"full_name":"Ryan Vergeront (F'13)","domain":"students.hackerschool.com","email":"ryanvergeront@gmail.com","short_name":"ryanvergeront","id":2874},{"domain":"students.hackerschool.com","short_name":"test_zulip_cli-bot","email":"test_zulip_cli-bot@students.hackerschool.com","full_name":"Ryan's Bot","id":2934}],"sender_id":2874,"sender_full_name":"Ryan Vergeront (F'13)","sender_domain":"students.hackerschool.com","content":"the messages are less funny when I'm PMing myself","gravatar_hash":"a8e2e659c929c79669f97603bd73fcc5","avatar_url":"https://secure.gravatar.com/avatar/a8e2e659c929c79669f97603bd73fcc5?d=identicon","client":"desktop app Mac 0.3.10","content_type":"text/x-markdown","subject_links":[],"sender_short_name":"ryanvergeront","type":"private","id":12855410,"subject":""},"type":"message","id":0}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","subscriptions":[{"name":"VIM","color":"#76ce90","notifications":false,"subscribers":["bob@example.com","alice@example.com"],"invite_only":false,"email_address":"VIM+c46cdd8670afe104a66e5f1af277aa05@streams.zulip.com","in_home_view":true},{"name":"social","color":"#94c849","notifications":false,"subscribers":["alice@example.com","jane@example.com"],"invite_only":false,"email_address":"social+06435131f9e681a9835c2733893baad2@streams.zulip.com","in_home_view":true}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","members":[{"email":"bob@example.org","full_name":"Bob G"},{"email":"alice@example.org","full_name":"Alice P"}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","id":12743014}
@@ -0,0 +1 @@
1
+ {"msg":"","max_message_id":12779330,"last_event_id":-1,"result":"success","queue_id":"1384438093:1161"}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","id":12745890}
data/spec/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+
3
+ def parsed_json_fixture(file)
4
+ JSON.parse(fixture(file))
5
+ end
6
+
7
+ def fixture(file)
8
+ File.new(fixture_path + '/' + file).read
9
+ end
10
+
11
+ def fixture_path
12
+ File.expand_path("../fixtures", __FILE__)
13
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "../../../../lib/zulip"
2
+ require 'helper'
3
+
4
+ module Zulip
5
+ class Client
6
+ describe EventParser do
7
+ it "parses message events" do
8
+ event_hash = parsed_json_fixture("get-message-event-success.json")['events'].first
9
+ result = Zulip::Client::EventParser.parse(event_hash)
10
+ expect(result).to be_kind_of Zulip::Message
11
+ end
12
+
13
+ it "parses heartbeat events"
14
+ it "parses presence events"
15
+ it "parses subscriptions events"
16
+ it "parses error events"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,85 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ class ExitStreamingGracefullyError < StandardError; end
5
+
6
+ module Zulip
7
+ class Client
8
+ describe EventStreaming do
9
+ let(:private_message_fixture) { fixture("get-private-message.json") }
10
+ let(:public_message_fixture) { fixture("get-message-event-success.json") }
11
+
12
+ context "streaming messages" do
13
+
14
+ let(:client) { Zulip::Client.new }
15
+ let(:fake_connection) { double("fake connection", :params= => nil) }
16
+ let(:fake_response) { double("response") }
17
+ let(:fake_queue) { double("fake queue", queue_id: "id", last_event_id: -1) }
18
+
19
+ describe "#stream_private_messages" do
20
+ it "returns private messages viewable to the bot" do
21
+
22
+ # Returns a public-message, then a private message
23
+ fake_response.stub(:body).and_return(public_message_fixture, private_message_fixture)
24
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
25
+ client.stub(:register).and_return(fake_queue)
26
+
27
+ client.connection = fake_connection
28
+
29
+ messages = []
30
+ begin
31
+ client.stream_private_messages do |msg|
32
+ messages << msg
33
+ raise ExitStreamingGracefullyError if messages.count == 2
34
+ end
35
+ rescue ExitStreamingGracefullyError
36
+ messages.each { |msg| expect(msg.type).to eq("private") }
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#stream_public_messages" do
42
+ it "only returns public messages" do
43
+ fake_response.stub(:body).and_return(private_message_fixture, public_message_fixture)
44
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
45
+ client.stub(:register).and_return(fake_queue)
46
+
47
+ client.connection = fake_connection
48
+
49
+ messages = []
50
+ begin
51
+ client.stream_public_messages do |msg|
52
+ messages << msg
53
+ raise ExitStreamingGracefullyError if messages.count == 1
54
+ end
55
+ rescue ExitStreamingGracefullyError
56
+ messages.each { |msg| expect(msg.type).to eq("stream") }
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#stream_messages" do
62
+ it "streams both public and private messages" do
63
+ fake_response.stub(:body).and_return(public_message_fixture, private_message_fixture)
64
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
65
+ client.stub(:register).and_return(fake_queue)
66
+
67
+ client.connection = fake_connection
68
+
69
+ messages = []
70
+ begin
71
+ client.stream_messages do |msg|
72
+ messages << msg
73
+ raise ExitStreamingGracefullyError if messages.count == 2
74
+ end
75
+ rescue ExitStreamingGracefullyError
76
+ expect(messages.first.type).to eq "stream"
77
+ expect(messages.last.type).to eq "private"
78
+ end
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe Messages do
7
+ it "posts a message to a stream" do
8
+ subject = "sending some internet websites to the internet"
9
+ content = "Hey, I found this on the internet: http://nbapaintings.blogspot.com/"
10
+ stream = "test-stream"
11
+
12
+ client = Zulip::Client.new
13
+ fake_response = fixture("post-message-to-stream.json")
14
+ fake_connection = double("fake connection", post: fake_response )
15
+ client.connection = fake_connection
16
+
17
+ fake_connection.should_receive(:params=).with({"type"=>"stream", "content"=>content,
18
+ "to" => json_encode_list(stream),
19
+ "subject"=>subject})
20
+
21
+ fake_connection.should_receive(:post).with("v1/messages")
22
+ client.send_message(subject, content, stream)
23
+ end
24
+
25
+ it "sends private messages to a single user" do
26
+ content = "Hi, I heard you like the internet. Here: http://en.wikipedia.org/wiki/Zebroid"
27
+ recipient = "internet_cat@example.com"
28
+
29
+ client = Zulip::Client.new
30
+
31
+ fake_response = fixture("sending-private-message-success.json")
32
+ fake_connection = double("fake connection", post: fake_response )
33
+ client.connection = fake_connection
34
+
35
+ fake_connection.should_receive(:params=).with({"type"=>"private", "content"=>content,
36
+ "to" => json_encode_list(recipient)})
37
+ fake_connection.should_receive(:post).with("v1/messages")
38
+ client.send_private_message(content, recipient)
39
+ end
40
+
41
+ it "sends private messages to multiple users" do
42
+
43
+ content = 'testing private messages to multiple users'
44
+ recipients = ["bob@gmail.com", "alice@gmail.com"]
45
+
46
+ client = Zulip::Client.new
47
+
48
+ fake_response = fixture("sending-private-message-success.json")
49
+ fake_connection = double("fake connection", post: fake_response )
50
+ client.connection = fake_connection
51
+
52
+ fake_connection.should_receive(:params=).with({"type"=>"private", "content"=>content,
53
+ "to" => json_encode_list(recipients)})
54
+ fake_connection.should_receive(:post).with("v1/messages")
55
+
56
+ client.send_private_message(content, recipients)
57
+ end
58
+
59
+ def json_encode_list(item)
60
+ JSON.generate(Array(item).flatten)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe QueueRegistration do
7
+ describe "registering a queue to stream messages" do
8
+ context "with an event type of messages" do
9
+ it "posts to the register api" do
10
+ fake_connection = double("fake connection")
11
+ fake_response = double("response", body: fixture("queue-registration-success.json"))
12
+ fake_connection.stub(:post).with("v1/register").and_return(fake_response)
13
+
14
+ client = Zulip::Client.new
15
+ client.connection = fake_connection
16
+
17
+ fake_connection.should_receive(:params=).with( { "event_types" => '["message"]' } )
18
+ fake_connection.should_receive(:post).with("v1/register")
19
+
20
+ stream_type = :message
21
+ client.register(stream_type)
22
+ end
23
+
24
+ it "returns an object with the queue's id and last event id" do
25
+ fake_response = double("response", body: fixture("queue-registration-success.json"))
26
+ fake_connection = double("fake connection", :params= => nil )
27
+ fake_connection.stub(:post).with("v1/register").and_return(fake_response)
28
+
29
+ client = Zulip::Client.new
30
+ client.connection = fake_connection
31
+
32
+ the_expected_queue_id = "1384438093:1161"
33
+ the_expected_last_event_id = -1
34
+ stream_type = :message
35
+ response = client.register(stream_type)
36
+
37
+ expect(response.queue_id).to eq the_expected_queue_id
38
+ expect(response.last_event_id).to eq the_expected_last_event_id
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe StreamSubscriptions do
7
+ describe "#get_subscriptions" do
8
+ it "returns the bot's stream subscriptions" do
9
+ fake_http_response = double("fake http response",
10
+ body: fixture("get-user-subscriptions.json"))
11
+ fake_connection = double("fake connection", get: fake_http_response)
12
+
13
+ client = Zulip::Client.new
14
+ client.connection = fake_connection
15
+
16
+ subscriptions = client.get_subscriptions
17
+ expect(subscriptions.first).to be_a Zulip::StreamSubscription
18
+ expect(subscriptions.last).to be_a Zulip::StreamSubscription
19
+ end
20
+ end
21
+
22
+ describe "#subscribe" do
23
+ it "sends a PATCH message to subscribe the user to a stream" do
24
+ fake_http_response = double("fake response",
25
+ body: fixture("add-subscription.json"))
26
+ fake_connection = double("fake connection", patch: fake_http_response)
27
+
28
+ client = Zulip::Client.new
29
+ client.connection = fake_connection
30
+
31
+ expected_params = ["v1/users/me/subscriptions",
32
+ {:add=>"[{\"name\":\"clojure\"},{\"name\":\"ruby\"}]"}]
33
+ fake_connection.should_receive(:patch).with(*expected_params)
34
+
35
+ streams = ["clojure", "ruby"]
36
+ client.subscribe(streams)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe Users do
7
+ describe "get_users" do
8
+ it "returns users in the bot's realm" do
9
+
10
+ fake_http_response = double("fake http response", body: fixture("get-users.json"))
11
+ fake_connection = double("fake connection", get: fake_http_response)
12
+
13
+ client = Zulip::Client.new
14
+ client.connection = fake_connection
15
+
16
+ users = client.get_users
17
+ expect(users.first).to be_kind_of Zulip::User
18
+ expect(users.last).to be_kind_of Zulip::User
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ describe Zulip::Client do
4
+ describe "configuration" do
5
+ it "yields itself upon initialization for configuration" do
6
+
7
+ client = Zulip::Client.new do |config|
8
+ config.bot_email_address = "bot@example.com"
9
+ config.api_key = "apikey"
10
+ end
11
+
12
+ expect(client.bot_email_address).to eq "bot@example.com"
13
+ expect(client.api_key).to eq "apikey"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,79 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe Message do
5
+ let(:message_data) { {
6
+ "recipient_id"=>23442,
7
+ "sender_email"=>"ryanvergeront@gmail.com",
8
+ "timestamp"=>1384460690,
9
+ "display_recipient"=>"test-stream",
10
+ "sender_id"=>2874,
11
+ "sender_full_name"=>"Ryan Vergeront (F'13)",
12
+ "sender_domain"=>"students.hackerschool.com",
13
+ "content"=>"register an event type of 'message' not 'messages'",
14
+ "gravatar_hash"=>"a8e2e659c929c79669f97603bd73fcc5",
15
+ "avatar_url"=>
16
+ "https://secure.gravatar.com/avatar/a8e2e659c929c79669f97603bd73fcc5?d=identicon",
17
+ "client"=>"desktop app Mac 0.3.10",
18
+ "content_type"=>"text/x-markdown",
19
+ "subject_links"=>[],
20
+ "sender_short_name"=>"ryanvergeront",
21
+ "type"=>"stream",
22
+ "id"=>12791579,
23
+ "subject"=>"sending some internet websites to the internet" } }
24
+
25
+ it "implements attr_readers for it's attributes" do
26
+ message = Message.new(message_data)
27
+ expect(message.sender_email).to eq "ryanvergeront@gmail.com"
28
+
29
+ message_data.keys.each do |name|
30
+ return_value = message.send(name.to_sym)
31
+ expect(return_value).not_to be_nil
32
+ end
33
+ expect(message.stream).to eq "test-stream"
34
+ end
35
+
36
+ let(:pm_data) { {"recipient_id"=>25104,
37
+ "sender_email"=>"test_zulip_cli-bot@students.hackerschool.com",
38
+ "timestamp"=>1384536182,
39
+ "display_recipient"=>
40
+ [{"domain"=>"students.hackerschool.com",
41
+ "short_name"=>"ryanvergeront",
42
+ "email"=>"ryanvergeront@gmail.com",
43
+ "full_name"=>"Ryan Vergeront (F'13)",
44
+ "id"=>2874},
45
+ {"full_name"=>"Ryan's Bot",
46
+ "domain"=>"students.hackerschool.com",
47
+ "email"=>"test_zulip_cli-bot@students.hackerschool.com",
48
+ "short_name"=>"test_zulip_cli-bot",
49
+ "id"=>2934}],
50
+ "sender_id"=>2934,
51
+ "sender_full_name"=>"Ryan's Bot",
52
+ "sender_domain"=>"students.hackerschool.com",
53
+ "content"=>
54
+ "Ryan Vergeront (F'13), posted to test-stream:\n sending some internet websites to the internet: :)",
55
+ "gravatar_hash"=>"9edc706c9ea089025a4bede65630866b",
56
+ "avatar_url"=>
57
+ "https://secure.gravatar.com/avatar/9edc706c9ea089025a4bede65630866b?d=identicon",
58
+ "client"=>"API",
59
+ "content_type"=>"text/x-markdown",
60
+ "subject_links"=>[],
61
+ "sender_short_name"=>"test_zulip_cli-bot",
62
+ "type"=>"private",
63
+ "id"=>12836310,
64
+ "subject"=>""} }
65
+
66
+ it "works for private messages" do
67
+ pm = Message.new(pm_data)
68
+ expect(pm.display_recipients).to be_a(Array)
69
+
70
+ pm_data.each do |name, value|
71
+ unless value.class == Array
72
+ return_value = pm.send(name.to_sym)
73
+ expect(return_value).not_to be_nil
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe StreamSubscription do
5
+ let(:data) { { "name"=>"VIM",
6
+ "color"=>"#76ce90",
7
+ "notifications"=>false,
8
+ "subscribers"=>["bob@example.com", "alice@example.com"],
9
+ "invite_only"=>false,
10
+ "email_address"=>"VIM@streams.zulip.com",
11
+ "in_home_view"=>true } }
12
+
13
+ it "has attrs" do
14
+ subscription = StreamSubscription.new(data)
15
+ data.each do |key, value|
16
+ expect(subscription.send(key)).to eq value
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe User do
5
+ it "has an email" do
6
+ data = {"email"=>"bob@example.org","full_name"=>"Bob G"}
7
+ user = User.new(data)
8
+ expect(user.email).to eq "bob@example.org"
9
+ end
10
+
11
+ it "has a full name" do
12
+ data = {"email"=>"bob@example.org","full_name"=>"Bob G"}
13
+ user = User.new(data)
14
+ expect(user.full_name).to eq "Bob G"
15
+ end
16
+ end
17
+ end
data/zulip-rb.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zulip/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zulip"
8
+ spec.version = Zulip::VERSION
9
+ spec.authors = ["Ryan Vergeront"]
10
+ spec.email = ["ryan.vergeront@gmail.com"]
11
+ spec.description = %q{A ruby wrapper for the Zulip API.}
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/verg/zulip-rb"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency('faraday', ['>= 0.7', '< 0.9'])
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency('rspec', '~> 2.4')
25
+ spec.add_development_dependency "pry"
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zulip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Vergeront
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: '0.9'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0.7'
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '1.3'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: '2.4'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: '2.4'
75
+ - !ruby/object:Gem::Dependency
76
+ name: pry
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: A ruby wrapper for the Zulip API.
90
+ email:
91
+ - ryan.vergeront@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - .gitignore
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - example/zulip_message_echo.rb
102
+ - lib/zulip.rb
103
+ - lib/zulip/client.rb
104
+ - lib/zulip/client/event_parser.rb
105
+ - lib/zulip/client/event_streaming.rb
106
+ - lib/zulip/client/messages.rb
107
+ - lib/zulip/client/queue_registration.rb
108
+ - lib/zulip/client/stream_subscriptions.rb
109
+ - lib/zulip/client/users.rb
110
+ - lib/zulip/message.rb
111
+ - lib/zulip/stream_subscription.rb
112
+ - lib/zulip/user.rb
113
+ - lib/zulip/version.rb
114
+ - spec/fixtures/add-subscription.json
115
+ - spec/fixtures/get-message-event-success.json
116
+ - spec/fixtures/get-private-message.json
117
+ - spec/fixtures/get-user-subscriptions.json
118
+ - spec/fixtures/get-users.json
119
+ - spec/fixtures/post-message-to-stream.json
120
+ - spec/fixtures/queue-registration-success.json
121
+ - spec/fixtures/sending-private-message-success.json
122
+ - spec/helper.rb
123
+ - spec/lib/zulip/client/event_parser_spec.rb
124
+ - spec/lib/zulip/client/event_streaming_spec.rb
125
+ - spec/lib/zulip/client/messages_spec.rb
126
+ - spec/lib/zulip/client/queue_registration_spec.rb
127
+ - spec/lib/zulip/client/stream_subscriptions_spec.rb
128
+ - spec/lib/zulip/client/users_spec.rb
129
+ - spec/lib/zulip/client_spec.rb
130
+ - spec/lib/zulip/message_spec.rb
131
+ - spec/lib/zulip/stream_subscription_spec.rb
132
+ - spec/lib/zulip/user_spec.rb
133
+ - zulip-rb.gemspec
134
+ homepage: https://github.com/verg/zulip-rb
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.0.5
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: A ruby wrapper for the Zulip API.
158
+ test_files:
159
+ - spec/fixtures/add-subscription.json
160
+ - spec/fixtures/get-message-event-success.json
161
+ - spec/fixtures/get-private-message.json
162
+ - spec/fixtures/get-user-subscriptions.json
163
+ - spec/fixtures/get-users.json
164
+ - spec/fixtures/post-message-to-stream.json
165
+ - spec/fixtures/queue-registration-success.json
166
+ - spec/fixtures/sending-private-message-success.json
167
+ - spec/helper.rb
168
+ - spec/lib/zulip/client/event_parser_spec.rb
169
+ - spec/lib/zulip/client/event_streaming_spec.rb
170
+ - spec/lib/zulip/client/messages_spec.rb
171
+ - spec/lib/zulip/client/queue_registration_spec.rb
172
+ - spec/lib/zulip/client/stream_subscriptions_spec.rb
173
+ - spec/lib/zulip/client/users_spec.rb
174
+ - spec/lib/zulip/client_spec.rb
175
+ - spec/lib/zulip/message_spec.rb
176
+ - spec/lib/zulip/stream_subscription_spec.rb
177
+ - spec/lib/zulip/user_spec.rb