qismo 0.1.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/CODE_OF_CONDUCT.md DELETED
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at nurcholisart@gmail.com. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [https://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: https://contributor-covenant.org
74
- [version]: https://contributor-covenant.org/version/1/4/
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2021 Nurcholis Art
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/lib/qismo/errors.rb DELETED
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Qismo generic error
6
- # @!attribute message [String]
7
- #
8
- class Error < StandardError
9
- end
10
-
11
- #
12
- # Qismo HTTP error
13
- #
14
- # @attribute message [String]
15
- # @attribute http_code [Integer]
16
- # @attribute http_headers [Hash]
17
- # @attribute http_body [Hash,String]
18
- # @attribute http_raw_body [String]
19
- #
20
- class HTTPError < StandardError
21
- attr_accessor :message, :http_code, :http_headers, :http_body, :http_raw_body
22
-
23
- def initialize(message = nil, http_code: nil, http_headers: nil, http_body: nil, http_raw_body: nil)
24
- @message = message
25
- @http_code = http_code
26
- @http_headers = http_headers
27
- @http_body = http_body
28
- @http_raw_body = http_raw_body
29
- end
30
- end
31
-
32
- # Error for 5xx status code
33
- ServerError = Class.new(HTTPError)
34
-
35
- # Error for 500 status code
36
- InternalServerError = Class.new(ServerError)
37
-
38
- # Error for 4xx status code
39
- ClientError = Class.new(HTTPError)
40
-
41
- # Error for 400 status code
42
- BadRequest = Class.new(ClientError)
43
-
44
- # Error for 401 status code
45
- Unauthorized = Class.new(ClientError)
46
-
47
- # Error for 402 status code
48
- PaymentRequired = Class.new(ClientError)
49
-
50
- # Error for 403 status code
51
- Forbidden = Class.new(ClientError)
52
-
53
- # Error for 404 status code
54
- NotFound = Class.new(ClientError)
55
-
56
- # Error for 429 status code
57
- RateLimitExceeded = Class.new(ClientError)
58
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Utilities for Qismo Ruby client
6
- #
7
- module Helpers
8
- module BaseHelper
9
- #
10
- # Validate the existence of hash attributes
11
- #
12
- # @param args [Array<Symbol,String>]
13
- # @param on [Hash]
14
- #
15
- #
16
- def validate_existence_of!(*args, on:)
17
- args.each do |arg|
18
- falsy = (on[arg] == {} || on[arg] == [] || on[arg].nil? || on[arg] == "" || on[arg] == 0)
19
- raise BadRequest, "#{arg} is required" if falsy
20
- end
21
- end
22
-
23
- #
24
- # Check the argument is false or not
25
- #
26
- # @param arg [String, Integer, Hash, Array, NilClass]
27
- #
28
- # @return [TrueClass, FalseClass]
29
- #
30
- def falsy?(arg)
31
- (arg == {} || arg == [] || arg.nil? || arg == "" || arg == 0)
32
- end
33
-
34
- #
35
- # Safely parse json string
36
- #
37
- # @param str [String]
38
- #
39
- # @return [Hash,String]
40
- #
41
- def safe_parse_json(str, symbolize_names: false)
42
- str = str.to_json if str.is_a?(Hash)
43
-
44
- JSON.parse(str, symbolize_names: symbolize_names)
45
- rescue JSON::ParserError
46
- str
47
- end
48
- end
49
- end
50
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Agent model
6
- #
7
- # @!attribute id [Integer]
8
- # @!attribute email [String]
9
- # @!attribute name [String]
10
- # @!attribute authentication_token [String]
11
- # @!attribute created_at [String]
12
- # @!attribute updated_at [String]
13
- # @!attribute sdk_email [String]
14
- # @!attribute sdk_key [String]
15
- # @!attribute is_available [TrueClass, FalseClass]
16
- # @!attribute type [Integer]
17
- # @!attribute avatar_url [String]
18
- # @!attribute app_id [Integer]
19
- # @!attribute is_verified [TrueClass, FalseClass]
20
- # @!attribute notifications_room_id [String]
21
- # @!attribute bubble_color [String, nil]
22
- # @!attribute qismo_key [String]
23
- # @!attribute direct_login_token [String]
24
- # @!attribute last_login [String]
25
- # @!attribute force_offline [TrueClass, FalseClass]
26
- # @!attribute deleted_at [String, nil]
27
- # @!attribute type_as_string [String]
28
- # @!attribute assigned_rules [Array<String>]
29
- #
30
- class Agent < Base
31
- # @!parse
32
- # extend Resources::AgentResource
33
- extend Resources::AgentResource
34
- end
35
-
36
- module Operations
37
- # @return [Qismo::Agent::Class]
38
- def agent
39
- Qismo::Agent
40
- end
41
- end
42
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Base object of Qismo object
6
- #
7
- class Base
8
- #
9
- # Transform object to hash
10
- #
11
- # @return [Hash]
12
- #
13
- def as_json
14
- hash = {}
15
- instance_variables.each { |var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
16
-
17
- hash
18
- end
19
-
20
- # @return [Array<Object>]
21
- def self.descendants
22
- ObjectSpace.each_object(Class).select { |klass| klass < self }
23
- end
24
-
25
- private
26
-
27
- #
28
- # Initialize object
29
- #
30
- # @param attrs [Hash] Attributes of object
31
- #
32
- def initialize(attrs = {})
33
- attrs.each do |key, value|
34
- initiate_object(key, value)
35
- end
36
- end
37
-
38
- #
39
- # Dynamically tranform hash to class attributes
40
- #
41
- # @param key [String,Symbol]
42
- # @param value [String,Integer,TrueClass,FalseClass,Hash,Array]
43
- #
44
- #
45
- def initiate_object(key, value)
46
- if value.is_a?(TrueClass) || value.is_a?(FalseClass)
47
- key_s = key.to_s
48
- method_name = key_s.start_with?("is_") ? key_s.gsub("is_", "") : key_s
49
- method_name += "?"
50
-
51
- self.class.define_method(method_name) do
52
- value
53
- end
54
- end
55
-
56
- instance_variable_set("@#{key}", value)
57
- self.class.class_eval { attr_reader(key.to_sym) }
58
- end
59
- end
60
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Qismo bot object
6
- #
7
- class Bot
8
- # @!parse
9
- # extend Resources::BotResource
10
- extend Resources::BotResource
11
- end
12
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Class to handle nil value
6
- #
7
- class NullObject
8
- #
9
- # @return [TrueClass,FalseClass]
10
- #
11
- def !
12
- true
13
- end
14
-
15
- #
16
- # @return [TrueClass,FalseClass]
17
- #
18
- def respond_to?(*)
19
- true
20
- end
21
-
22
- #
23
- # @return [TrueClass,FalseClass]
24
- #
25
- def instance_of?(klass)
26
- raise(TypeError, "class or module required") unless klass.is_a?(Class)
27
-
28
- self.class == klass
29
- end
30
-
31
- #
32
- # @return [TrueClass,FalseClass]
33
- #
34
- def kind_of?(mod)
35
- raise(TypeError, "class or module required") unless mod.is_a?(Module)
36
-
37
- self.class.ancestors.include?(mod)
38
- end
39
-
40
- alias_method :is_a?, :kind_of?
41
-
42
- #
43
- # @return [Integer]
44
- #
45
- def <=>(other)
46
- if other.is_a?(self.class)
47
- 0
48
- else
49
- -1
50
- end
51
- end
52
-
53
- #
54
- # @return [TrueClass,FalseClass]
55
- #
56
- def nil?
57
- true
58
- end
59
-
60
- #
61
- # @return [String]
62
- #
63
- def as_json(*)
64
- "null"
65
- end
66
-
67
- #
68
- # @return [String]
69
- #
70
- def to_json(*args)
71
- nil.to_json(*args)
72
- end
73
-
74
- #
75
- # @return [nil]
76
- #
77
- def presence
78
- nil
79
- end
80
-
81
- #
82
- # @return [TrueClass,FalseClass]
83
- #
84
- def blank?
85
- true
86
- end
87
-
88
- #
89
- # @return [TrueClass,FalseClass]
90
- #
91
- def present?
92
- false
93
- end
94
-
95
- #
96
- # @return [NullObject]
97
- #
98
- def method_missing(m, *args, &block)
99
- self
100
- end
101
-
102
- #
103
- # @return [TrueClass,FalseClass]
104
- #
105
- def respond_to_missing?(method_name, *args)
106
- false
107
- end
108
- end
109
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Office hour model
6
- #
7
- # @!attribute day [Integer]
8
- # @!attribute starttime [String]
9
- # @!attribute endtime [String]
10
- #
11
- class OfficeHour < Base
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Office setting model
6
- #
7
- # @!attribute online_message [String]
8
- # @!attribute offline_message [String]
9
- # @!attribute timezone [String]
10
- # @!attribute send_online_if_resolved [TrueClass, FalseClass]
11
- # @!attribute send_offline_each_message [TrueClass, FalseClass]
12
- # @!attribute is_in_office_hour [TrueClass, FalseClass]
13
- # @!attribute office_hours [Array<OfficeHour>]
14
- class OfficeSetting < Base
15
- # @!parse
16
- # extend Resources::OfficeSettingResource
17
- extend Resources::OfficeSettingResource
18
- end
19
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # Room model
6
- #
7
- # @!attribute channel_id [Integer]
8
- # @!attribute contact_id [Integer]
9
- # @!attribute is_handled_by_bot [Truelass, FalseClass]
10
- # @!attribute is_resolved [Truelass, FalseClass]
11
- # @!attribute is_waiting [Truelass, FalseClass]
12
- # @!attribute last_comment_sender [String]
13
- # @!attribute last_comment_sender_type [String]
14
- # @!attribute last_comment_text [String]
15
- # @!attribute last_comment_timestamp [String]
16
- # @!attribute last_customer_comment_text [String, nil]
17
- # @!attribute last_customer_timestamp [String]
18
- # @!attribute name [String]
19
- # @!attribute room_badge [String]
20
- # @!attribute room_id [String]
21
- # @!attribute room_type [String]
22
- # @!attribute source [String]
23
- # @!attribute user_avatar_url [String]
24
- # @!attribute user_id [String]
25
- #
26
- class Room < Base
27
- # @!parse
28
- # extend Resources::RoomResource
29
- extend Resources::RoomResource
30
- end
31
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- #
5
- # User model
6
- #
7
- # @!attribute id [Integer]
8
- # @!attribute name [String]
9
- # @!attribute authentication_token [String]
10
- # @!attribute is_available [TrueClass, FalseClass]
11
- # @!attribute direct_login_token [String]
12
- # @!attribute long_lived_token [String]
13
- #
14
- class User < Base
15
- # @!parse
16
- # extend Resources::UserResource
17
- extend Resources::UserResource
18
- end
19
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- module Operations
5
- end
6
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- module Resources
5
- module AgentResource
6
- #
7
- # List agents
8
- #
9
- # @param options [Hash]
10
- # @option options [Integer] :page Page number
11
- # @option options [Integer] :limit Data offset per page
12
- # @option options [String] :search Filter agent by query
13
- # @option options [String] :scope Scoping search attribute. Can be `division`, `name`, or `email`
14
- #
15
- # @return [Array<Agent>]
16
- #
17
- def list(options = {})
18
- resp = Qismo.client.get("/api/v2/admin/agents", options)
19
- body = resp.http_body
20
- agents = body.dig("data", "agents")
21
- agents.dig("data", "agents").map { |agent| Agent.new(agent) }
22
- end
23
-
24
- #
25
- # Get agent by id
26
- #
27
- # @param id [Integer] Agent's id
28
- #
29
- # @return [Agent]
30
- #
31
- def get(id)
32
- resp = Qismo.client.call(:get, "/api/v2/admin/agent/#{id}")
33
- body = resp.http_body
34
- agent = body.dig("data", "agent")
35
- Agent.new(agent)
36
- end
37
- end
38
- end
39
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- module Resources
5
- module BotResource
6
- #
7
- # Send message to room as bot
8
- #
9
- # @param options [Hash]
10
- # @option options [String] :room_id
11
- # @option options [String] :message
12
- # @option options [String] :type required if type is not `text`
13
- # @!attribute payload [Hash] required if type is not `text`
14
- #
15
- # @return [Response]
16
- #
17
- def send_message(options = {})
18
- options[:sender_email] = Qismo.admin_email
19
-
20
- validate_existence_of!(:sender_email, :message, :room_id, on: options)
21
- options[:room_id] = options[:room_id].to_s
22
-
23
- Qismo.client.post("/#{Qismo.client.app_id}/bot", options)
24
- end
25
-
26
- #
27
- # Notify admin and/or agent that bot need human help
28
- #
29
- # @param room_id [Integer]
30
- # @param roles [Array<Integer>]
31
- # @param options [Hash]
32
- # @option options [TrueClass] :find_online_agent
33
- #
34
- # @return [Response]
35
- #
36
- def handover(room_id, *roles, **options)
37
- if roles.empty?
38
- Qismo.client.request(:post, "/#{Qismo.client.app_id}/bot/#{room_id}/hand_over", {
39
- headers: { authorization: Qismo.client.secret_key },
40
- })
41
- else
42
- int_roles = []
43
- roles.each { |role| int_roles.append(role) }
44
-
45
- if roles.size == 1
46
- options[:role] = int_roles.first
47
- else
48
- options[:roles] = int_roles
49
- end
50
-
51
- options = options.compact
52
-
53
- Qismo.client.request(:post, "/#{Qismo.client.app_id}/bot/#{room_id}/hand_over_to_role", {
54
- json: options,
55
- headers: { authorization: Qismo.client.secret_key },
56
- })
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qismo
4
- module Resources
5
- module OfficeSettingResource
6
- #
7
- # Get office hours
8
- #
9
- # @return [OfficeSetting]
10
- #
11
- def office_hours
12
- resp = Qismo.client.get("/api/v1/admin/office_hours")
13
- body = resp.http_body
14
-
15
- data = body["data"]
16
-
17
- morfed_data = {}
18
- morfed_data["online_message"] = data["online_message"]
19
- morfed_data["offline_message"] = data["offline_message"]
20
- morfed_data["timezone"] = data["timezone"]
21
- morfed_data["send_online_if_resolved"] = data["send_online_if_resolved"]
22
- morfed_data["send_offline_each_message"] = data["send_offline_each_message"]
23
-
24
- office_hours = []
25
- data["office_hours"].each do |oh|
26
- office_hours.append(OfficeHour.new(oh))
27
- end
28
-
29
- morfed_data["office_hours"] = office_hours
30
- morfed_data["is_in_office_hour"] = check_is_in_office_hour?(
31
- morfed_data["timezone"],
32
- morfed_data["office_hours"]
33
- )
34
-
35
- OfficeSetting.new(morfed_data)
36
- end
37
-
38
- private
39
-
40
- #
41
- # Check if current timestamp is in office hour
42
- #
43
- # @param timezone [String,Integer]
44
- # @param office_hours [Array<OfficeHour>]
45
- #
46
- # @return [TrueClass, FalseClass]
47
- #
48
- def check_is_in_office_hour?(timezone, office_hours)
49
- current = Time.now.getlocal(timezone)
50
- current_weekday = current.strftime("%u").to_i
51
-
52
- today_oh = office_hours.find { |oh| oh.day == current_weekday }
53
- return false if today_oh.nil?
54
-
55
- start_time = Time.parse("#{current.year}-#{current.month}-#{current.day} #{today_oh.starttime} #{timezone}")
56
- end_time = Time.parse("#{current.year}-#{current.month}-#{current.day} #{today_oh.endtime} #{timezone}")
57
-
58
- int_start_time = start_time.to_i
59
- int_end_time = end_time.to_i
60
-
61
- int_current = current.to_i
62
-
63
- (int_start_time..int_end_time).include?(int_current)
64
- end
65
- end
66
- end
67
- end