bas 1.5.2 → 1.6.0

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +92 -50
  4. data/lib/bas/bot/base.rb +37 -54
  5. data/lib/bas/shared_storage/base.rb +35 -0
  6. data/lib/bas/shared_storage/default.rb +18 -0
  7. data/lib/bas/shared_storage/postgres.rb +95 -0
  8. data/lib/bas/shared_storage/types/read.rb +22 -0
  9. data/lib/bas/utils/discord/request.rb +15 -2
  10. data/lib/bas/utils/openai/run_assistant.rb +1 -0
  11. data/lib/bas/utils/postgres/request.rb +3 -1
  12. data/lib/bas/version.rb +1 -1
  13. metadata +8 -41
  14. data/lib/bas/bot/compare_wip_limit_count.rb +0 -92
  15. data/lib/bas/bot/create_work_item.rb +0 -142
  16. data/lib/bas/bot/fetch_billing_from_digital_ocean.rb +0 -87
  17. data/lib/bas/bot/fetch_birthdays_from_notion.rb +0 -128
  18. data/lib/bas/bot/fetch_domain_services_from_notion.rb +0 -93
  19. data/lib/bas/bot/fetch_domains_wip_counts_from_notion.rb +0 -121
  20. data/lib/bas/bot/fetch_domains_wip_limit_from_notion.rb +0 -134
  21. data/lib/bas/bot/fetch_emails_from_imap.rb +0 -99
  22. data/lib/bas/bot/fetch_github_issues.rb +0 -147
  23. data/lib/bas/bot/fetch_images_from_discord.rb +0 -78
  24. data/lib/bas/bot/fetch_media_from_notion.rb +0 -186
  25. data/lib/bas/bot/fetch_next_week_birthdays_from_notion.rb +0 -142
  26. data/lib/bas/bot/fetch_next_week_ptos_from_notion.rb +0 -197
  27. data/lib/bas/bot/fetch_ptos_from_notion.rb +0 -160
  28. data/lib/bas/bot/format_birthdays.rb +0 -97
  29. data/lib/bas/bot/format_do_bill_alert.rb +0 -108
  30. data/lib/bas/bot/format_emails.rb +0 -124
  31. data/lib/bas/bot/format_wip_limit_exceeded.rb +0 -97
  32. data/lib/bas/bot/garbage_collector.rb +0 -85
  33. data/lib/bas/bot/humanize_pto.rb +0 -117
  34. data/lib/bas/bot/notify_discord.rb +0 -96
  35. data/lib/bas/bot/notify_do_bill_alert_email.rb +0 -94
  36. data/lib/bas/bot/review_domain_availability.rb +0 -96
  37. data/lib/bas/bot/review_media.rb +0 -139
  38. data/lib/bas/bot/update_review_media_state.rb +0 -102
  39. data/lib/bas/bot/update_work_item.rb +0 -181
  40. data/lib/bas/bot/verify_issue_existance_in_notion.rb +0 -131
  41. data/lib/bas/bot/write_domain_review_requests.rb +0 -104
  42. data/lib/bas/bot/write_media_review_in_discord.rb +0 -114
  43. data/lib/bas/bot/write_media_review_requests.rb +0 -104
  44. data/lib/bas/read/base.rb +0 -30
  45. data/lib/bas/read/default.rb +0 -16
  46. data/lib/bas/read/postgres.rb +0 -44
  47. data/lib/bas/read/types/response.rb +0 -18
  48. data/lib/bas/write/base.rb +0 -31
  49. data/lib/bas/write/postgres.rb +0 -45
  50. data/lib/bas/write/postgres_update.rb +0 -49
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "./base"
4
- require_relative "../read/postgres"
5
- require_relative "../utils/notion/request"
6
- require_relative "../write/postgres"
7
-
8
- module Bot
9
- ##
10
- # The Bot::FetchDomainsWipLimitFromNotion class serves as a bot implementation to fetch domains wip
11
- # limits from a Notion database, merge them with the count of how many are by domain, and write them
12
- # on a PostgresDB table with a specific format.
13
- #
14
- # <br>
15
- # <b>Example</b>
16
- #
17
- # options = {
18
- # read_options: {
19
- # connection: {
20
- # host: "host",
21
- # port: 5432,
22
- # dbname: "bas",
23
- # user: "postgres",
24
- # password: "postgres"
25
- # },
26
- # db_table: "use_cases",
27
- # tag: "FetchDomainsWipCountsFromNotion"
28
- # },
29
- # process_options: {
30
- # database_id: "notion database id",
31
- # secret: "notion secret"
32
- # },
33
- # write_options: {
34
- # connection: {
35
- # host: "host",
36
- # port: 5432,
37
- # dbname: "bas",
38
- # user: "postgres",
39
- # password: "postgres"
40
- # },
41
- # db_table: "use_cases",
42
- # tag: "FetchDomainsWipLimitFromNotion"
43
- # }
44
- # }
45
- #
46
- # bot = Bot::FetchDomainsWipLimitFromNotion.new(options)
47
- # bot.execute
48
- #
49
- class FetchDomainsWipLimitFromNotion < Bot::Base
50
- # read function to execute the PostgresDB Read component
51
- #
52
- def read
53
- reader = Read::Postgres.new(read_options.merge(conditions))
54
-
55
- reader.execute
56
- end
57
-
58
- # Process function to execute the Notion utility to fetch domain wip limits from the notion database
59
- #
60
- def process
61
- response = Utils::Notion::Request.execute(params)
62
-
63
- if response.code == 200
64
- domains_limits = normalize_response(response.parsed_response["results"])
65
-
66
- wip_limit_data = wip_count.merge({ domains_limits: })
67
-
68
- { success: wip_limit_data }
69
- else
70
- { error: { message: response.parsed_response, status_code: response.code } }
71
- end
72
- end
73
-
74
- # Write function to execute the PostgresDB write component
75
- #
76
- def write
77
- write = Write::Postgres.new(write_options, process_response)
78
-
79
- write.execute
80
- end
81
-
82
- private
83
-
84
- def conditions
85
- {
86
- where: "archived=$1 AND tag=$2 AND stage=$3 ORDER BY inserted_at ASC",
87
- params: [false, read_options[:tag], "unprocessed"]
88
- }
89
- end
90
-
91
- def params
92
- {
93
- endpoint: "databases/#{process_options[:database_id]}/query",
94
- secret: process_options[:secret],
95
- method: "post",
96
- body:
97
- }
98
- end
99
-
100
- def body
101
- {
102
- filter: {
103
- property: "WIP + On Hold limit",
104
- number: { is_not_empty: true }
105
- }
106
- }
107
- end
108
-
109
- def normalize_response(results)
110
- return [] if results.nil?
111
-
112
- results.reduce({}) do |domains_limits, domain_wip_limit|
113
- domain_fields = domain_wip_limit["properties"]
114
-
115
- domain = extract_domain_name_value(domain_fields["Name"])
116
- limit = extract_domain_limit_value(domain_fields["WIP + On Hold limit"])
117
-
118
- domains_limits.merge({ domain => limit })
119
- end
120
- end
121
-
122
- def extract_domain_name_value(data)
123
- data["title"].first["plain_text"]
124
- end
125
-
126
- def extract_domain_limit_value(data)
127
- data["number"]
128
- end
129
-
130
- def wip_count
131
- read_response.data.nil? ? {} : read_response.data
132
- end
133
- end
134
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "./base"
4
- require_relative "../read/default"
5
- require_relative "../utils/imap/request"
6
- require_relative "../write/postgres"
7
-
8
- module Bot
9
- ##
10
- # The Bot::FetchEmailsFromImap class serves as a bot implementation to fetch emails from a imap server
11
- # and write them on a PostgresDB table with a specific format.
12
- #
13
- # <br>
14
- # <b>Example</b>
15
- #
16
- # params = {
17
- # process_options: {
18
- # refresh_token: "email server refresh token",
19
- # client_id: "email server client it",
20
- # client_secret: "email server client secret",
21
- # token_uri: "email server refresh token uri",
22
- # email_domain: "email server domain",
23
- # email_port: "email server port",
24
- # user_email: "email to be access",
25
- # search_email: "email to be search",
26
- # inbox: "inbox to be search"
27
- # },
28
- # write_options: {
29
- # connection:,
30
- # db_table: "use_cases",
31
- # tag: "FetchEmailsFromImap"
32
- # }
33
- # }
34
- #
35
- # bot = Bot::FetchEmailsFromImap.new(options)
36
- # bot.execute
37
- #
38
- class FetchEmailsFromImap < Bot::Base
39
- # Read function to execute the default Read component
40
- #
41
- def read
42
- reader = Read::Default.new
43
-
44
- reader.execute
45
- end
46
-
47
- # Process function to request email from an imap server using the imap utility
48
- #
49
- def process
50
- response = Utils::Imap::Request.new(process_options, query).execute
51
-
52
- if response[:error]
53
- { error: response }
54
- else
55
- emails = normalize_response(response[:emails])
56
-
57
- { success: { emails: } }
58
- end
59
- end
60
-
61
- # Write function to execute the PostgresDB write component
62
- #
63
- def write
64
- write = Write::Postgres.new(write_options, process_response)
65
-
66
- write.execute
67
- end
68
-
69
- private
70
-
71
- def query
72
- yesterday = (Time.now - (60 * 60 * 24)).strftime("%d-%b-%Y")
73
-
74
- ["TO", process_options[:search_email], "SINCE", yesterday]
75
- end
76
-
77
- def normalize_response(results)
78
- return [] if results.nil?
79
-
80
- results.map do |value|
81
- message = value[:message]
82
-
83
- {
84
- "message_id": value[:message_id],
85
- "sender" => extract_sender(message),
86
- "date" => message.date,
87
- "subject" => message.subject
88
- }
89
- end
90
- end
91
-
92
- def extract_sender(value)
93
- mailbox = value.sender[0]["mailbox"]
94
- host = value.sender[0]["host"]
95
-
96
- "#{mailbox}@#{host}"
97
- end
98
- end
99
- end
@@ -1,147 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "./base"
4
- require_relative "../read/postgres"
5
- require_relative "../utils/github/octokit_client"
6
- require_relative "../write/postgres"
7
-
8
- module Bot
9
- ##
10
- # The Bot::FetchGithubIssues class serves as a bot implementation to fetch GitHub issues from a
11
- # repository and write them on a PostgresDB table with a specific format.
12
- #
13
- # <br>
14
- # <b>Example</b>
15
- #
16
- # options = {
17
- # read_options: {
18
- # connection: {
19
- # host: "localhost",
20
- # port: 5432,
21
- # dbname: "bas",
22
- # user: "postgres",
23
- # password: "postgres"
24
- # },
25
- # db_table: "github_issues",
26
- # tag: "FetchGithubIssues",
27
- # avoid_process: true
28
- # },
29
- # process_options: {
30
- # private_pem: "Github App private token",
31
- # app_id: "Github App id",
32
- # repo: "repository name",
33
- # filters: "hash with filters",
34
- # organization: "GitHub organization name"
35
- # connection: {
36
- # host: "localhost",
37
- # port: 5432,
38
- # dbname: "bas",
39
- # user: "postgres",
40
- # password: "postgres"
41
- # },
42
- # db_table: "github_issues",
43
- # tag: "GithubIssueRequest"
44
- # },
45
- # write_options: {
46
- # connection: {
47
- # host: "localhost",
48
- # port: 5432,
49
- # dbname: "bas",
50
- # user: "postgres",
51
- # password: "postgres"
52
- # },
53
- # db_table: "github_issues",
54
- # tag: "FetchGithubIssues"
55
- # }
56
- # }
57
- #
58
- # bot = Bot::FetchGithubIssues.new(options)
59
- # bot.execute
60
- #
61
- class FetchGithubIssues < Bot::Base
62
- ISSUE_PARAMS = %i[id html_url title body labels state created_at updated_at state].freeze
63
- PER_PAGE = 100
64
-
65
- # read function to execute the PostgresDB Read component
66
- #
67
- def read
68
- reader = Read::Postgres.new(read_options.merge(conditions))
69
-
70
- reader.execute
71
- end
72
-
73
- # Process function to request GitHub issues using the octokit utility
74
- #
75
- def process
76
- octokit = Utils::Github::OctokitClient.new(params).execute
77
-
78
- if octokit[:client]
79
- repo_issues = octokit[:client].issues(@process_options[:repo], filters)
80
-
81
- normalize_response(repo_issues).each { |issue| create_request(issue) }
82
-
83
- { success: { created: true } }
84
- else
85
- { error: octokit[:error] }
86
- end
87
- end
88
-
89
- # Write function to execute the PostgresDB write component
90
- #
91
- def write
92
- write = Write::Postgres.new(write_options, process_response)
93
-
94
- write.execute
95
- end
96
-
97
- private
98
-
99
- def conditions
100
- {
101
- where: "tag=$1 ORDER BY inserted_at DESC",
102
- params: [read_options[:tag]]
103
- }
104
- end
105
-
106
- def params
107
- {
108
- private_pem: process_options[:private_pem],
109
- app_id: process_options[:app_id],
110
- method: process_options[:method],
111
- method_params: process_options[:method_params],
112
- organization: process_options[:organization]
113
- }
114
- end
115
-
116
- def filters
117
- default_filter = { per_page: PER_PAGE }
118
-
119
- filters = process_options[:filters]
120
- filters = filters.merge({ since: read_response.inserted_at }) unless read_response.nil?
121
-
122
- filters.is_a?(Hash) ? default_filter.merge(filters) : default_filter
123
- end
124
-
125
- def normalize_response(issues)
126
- issues.map do |issue|
127
- ISSUE_PARAMS.reduce({}) do |hash, param|
128
- hash.merge({ param => issue.send(param) })
129
- .merge({ assignees: issue.assignees.map(&:login) })
130
- end
131
- end
132
- end
133
-
134
- def create_request(issue)
135
- write_data = {
136
- success: {
137
- issue:,
138
- work_item_type: process_options[:work_item_type],
139
- type_id: process_options[:type_id],
140
- domain: process_options[:domain]
141
- }
142
- }
143
-
144
- Write::Postgres.new(process_options, write_data).execute
145
- end
146
- end
147
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "./base"
4
- require_relative "../read/default"
5
- require_relative "../utils/discord/request"
6
- require_relative "../write/postgres"
7
-
8
- module Bot
9
- ##
10
- # The Bot::FetchImagesFromDiscord class serves as a bot implementation to read images
11
- # from a any thread of Discord channel.
12
- #
13
- # <br>
14
- # <b>Example</b>
15
- #
16
- # options = {
17
- # process_options: {
18
- # secret_token: "discord_bot_token"
19
- # discord_channel: "discord_channel_id"
20
- # },
21
- # write_options: {
22
- # connection: {
23
- # host: "localhost",
24
- # port: 5432,
25
- # dbname: "bas",
26
- # user: "postgres",
27
- # password: "postgres"
28
- # },
29
- # db_table: "review_images",
30
- # tag: "FetchImagesFromDiscord"
31
- # }
32
- # }
33
- #
34
- # bot = Bot::FetchImagesFromDiscord.new(options)
35
- # bot.execute
36
- #
37
- class FetchImagesFromDiscord < Bot::Base
38
- # read function to execute the Default Read component
39
- #
40
- def read
41
- reader = Read::Default.new
42
-
43
- reader.execute
44
- end
45
-
46
- # Process function to execute the Discord utility to fetch images from a discord channel threads
47
- #
48
- def process
49
- response = Utils::Discord::Request.get_thread_messages(params)
50
-
51
- if !response.nil?
52
- { success: { results: response } }
53
- else
54
- { error: "response is empty" }
55
- end
56
- end
57
-
58
- # Write function to execute the PostgresDB write component
59
- #
60
- def write
61
- write = Write::Postgres.new(write_options, process_response)
62
-
63
- write.execute
64
- end
65
-
66
- private
67
-
68
- def params
69
- {
70
- endpoint: "channels/#{process_options[:discord_channel]}/messages",
71
- channel_id: process_options[:discord_channel],
72
- secret_token: process_options[:secret_token],
73
- method: "get",
74
- body: {}
75
- }
76
- end
77
- end
78
- end
@@ -1,186 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "./base"
4
- require_relative "../read/postgres"
5
- require_relative "../utils/notion/request"
6
- require_relative "../write/postgres"
7
-
8
- module Bot
9
- ##
10
- # The Bot::FetchMediaFromNotion class serves as a bot implementation to read media (text or images)
11
- # from a notion database and write them on a PostgresDB table with a specific format.
12
- #
13
- # <br>
14
- # <b>Example</b>
15
- #
16
- # options = {
17
- # process_options: {
18
- # database_id: "notion_database_id",
19
- # secret: "notion_secret",
20
- # property: "paragraph"
21
- # },
22
- # write_options: {
23
- # connection: {
24
- # host: "localhost",
25
- # port: 5432,
26
- # dbname: "bas",
27
- # user: "postgres",
28
- # password: "postgres"
29
- # },
30
- # db_table: "review_media",
31
- # tag: "FetchMediaFromNotion"
32
- # }
33
- # }
34
- #
35
- # bot = Bot::FetchMediaFromNotion.new(options)
36
- # bot.execute
37
- #
38
- class FetchMediaFromNotion < Bot::Base # rubocop:disable Metrics/ClassLength
39
- CONTENT_STATUS = "review"
40
-
41
- # read function to execute the PostgresDB Read component
42
- #
43
- def read
44
- reader = Read::Postgres.new(read_options.merge(conditions))
45
-
46
- reader.execute
47
- end
48
-
49
- # Process function to execute the Notion utility to fetch media from a notion database
50
- #
51
- def process
52
- media_response = fetch_media
53
-
54
- return media_response unless media_response[:error].nil?
55
-
56
- { success: media_response }
57
- end
58
-
59
- # Write function to execute the PostgresDB write component
60
- #
61
- def write
62
- write = Write::Postgres.new(write_options, process_response)
63
-
64
- write.execute
65
- end
66
-
67
- private
68
-
69
- def conditions
70
- {
71
- where: "archived=$1 AND tag=$2 ORDER BY inserted_at DESC",
72
- params: [false, read_options[:tag]]
73
- }
74
- end
75
-
76
- def fetch_media
77
- request_ids_response = fetch_requests_ids
78
-
79
- return request_ids_response unless request_ids_response[:error].nil?
80
-
81
- { results: fetch_pages(request_ids_response[:ids]) }
82
- end
83
-
84
- def fetch_requests_ids
85
- response = Utils::Notion::Request.execute(params_database)
86
-
87
- return error(response) unless response.code == 200
88
-
89
- { ids: response["results"]&.map { |result| result["id"] } }
90
- end
91
-
92
- def fetch_pages(pages_ids)
93
- pages_ids.map do |page_id|
94
- content_response = fetch_content(page_id)
95
-
96
- process_content(page_id, content_response)
97
- end
98
- end
99
-
100
- def process_content(page_id, content)
101
- return content unless content[:error].nil?
102
-
103
- filter_media(page_id, content[:results])
104
- end
105
-
106
- def filter_media(page_id, page_content)
107
- medias = page_content.select { |content| content["type"] == process_options[:property] }
108
-
109
- media = extract_content(medias)
110
- created_by = medias.first["created_by"]["id"] unless medias.first.nil?
111
-
112
- { media:, page_id:, created_by:, property: process_options[:property] }
113
- end
114
-
115
- def extract_content(content)
116
- case process_options[:property]
117
- when "image" then process_images(content)
118
- when "paragraph" then process_text(content)
119
- end
120
- end
121
-
122
- def process_images(images)
123
- images.map { |image| image["image"]["file"]["url"] }
124
- end
125
-
126
- def process_text(texts)
127
- texts.reduce("") do |paragraph, text|
128
- rich_text = text["paragraph"]["rich_text"].map { |plain_text| plain_text["plain_text"] }
129
-
130
- content = rich_text.empty? ? "" : rich_text.join(" ")
131
-
132
- "#{paragraph}\n#{content}"
133
- end
134
- end
135
-
136
- def fetch_content(block_id)
137
- response = fetch_block(block_id)
138
-
139
- return error(response) unless response.code == 200
140
-
141
- { results: response["results"] }
142
- end
143
-
144
- def error(response)
145
- { error: { message: response.parsed_response, status_code: response.code } }
146
- end
147
-
148
- def params_database
149
- {
150
- endpoint: "databases/#{process_options[:database_id]}/query",
151
- secret: process_options[:secret],
152
- method: "post",
153
- body: body_database
154
- }
155
- end
156
-
157
- def body_database
158
- { filter: { and: [] + property_condition + time_condition } }
159
- end
160
-
161
- def property_condition
162
- [{ property: process_options[:property], select: { equals: CONTENT_STATUS } }]
163
- end
164
-
165
- def time_condition
166
- return [] if read_response.inserted_at.nil?
167
-
168
- [{ property: "Last edited time", last_edited_time: { on_or_after: read_response.inserted_at } }]
169
- end
170
-
171
- def fetch_block(block_id)
172
- params = block_params(block_id)
173
-
174
- Utils::Notion::Request.execute(params)
175
- end
176
-
177
- def block_params(block_id)
178
- {
179
- endpoint: "blocks/#{block_id}/children",
180
- secret: process_options[:secret],
181
- method: "get",
182
- body: {}
183
- }
184
- end
185
- end
186
- end