ask-slack 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b550c9169e3b980cb31099c840db9da17a79d2b1164ef92ba3b6d85080a68f3f
4
+ data.tar.gz: 484183cfcf1a3b76e11f3c3b1d09382c2d4acc72e2d73c538c8b0448fd8c2d55
5
+ SHA512:
6
+ metadata.gz: ddf878f447dfd83a0130daf887c4b629fbaa5af92513d4908fdaf9789b787505e2bde3f9cd334ce8b3890d427d2fea350b53d2eb7d9955f9edf8c878519f9283
7
+ data.tar.gz: 80b75a4d986f37c174b46435641f623b3ff46267b0cf9263cc95478c5081da37b19bcfac10108b83aef35d12365ddc19ac03ca7b6d8f01afc5f0638b5cda2078
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaka Ruto
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # ask-slack
2
+
3
+ Slack service context for the ask-rb ecosystem.
4
+
5
+ Provides:
6
+ - `Ask::Slack.client` — authenticated Slack Web API client
7
+ - `Ask::Slack::DESCRIPTION` — context metadata for the system prompt
8
+ - `Ask::Slack::Errors` — structured error knowledge for agents
9
+
10
+ ## Installation
11
+
12
+ ```ruby
13
+ gem "ask-slack"
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```ruby
19
+ require "ask-slack"
20
+
21
+ client = Ask::Slack.client
22
+ client.chat_postMessage(channel: "#general", text: "Hello from ask-rb!")
23
+ client.conversations_list
24
+ client.users_list
25
+ ```
26
+
27
+ ## Authentication
28
+
29
+ Set your Slack Bot User OAuth Token:
30
+
31
+ ```bash
32
+ export SLACK_TOKEN=xoxb-your-bot-token-here
33
+ ```
34
+
35
+ Or add it to `~/.ask/credentials.yml`:
36
+
37
+ ```yaml
38
+ slack_token: xoxb-your-bot-token-here
39
+ ```
40
+
41
+ ## Development
42
+
43
+ ```bash
44
+ bundle install
45
+ bundle exec rake test
46
+ ```
47
+
48
+ ## License
49
+
50
+ MIT
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slack-ruby-client"
4
+ require "ask/auth"
5
+
6
+ module Ask
7
+ module Slack
8
+ # Returns an authenticated Slack Web API client configured for an AI agent.
9
+ #
10
+ # Resolves the Slack token via +Ask::Auth.resolve(:slack_token)+ and
11
+ # wraps the client in a proxy that converts +::Slack::Web::Api::Errors::SlackError+
12
+ # authentication errors into +Ask::Auth::InvalidCredential+.
13
+ #
14
+ # Configuration:
15
+ # - Uses the resolved token for authentication
16
+ # - Default user agent is set by slack-ruby-client
17
+ #
18
+ # @example
19
+ # client = Ask::Slack.client
20
+ # client.channels_list
21
+ # client.chat_postMessage(channel: "#general", text: "Hello!")
22
+ #
23
+ # @return [::Slack::Web::Client] an authenticated client
24
+ # @raise [Ask::Auth::MissingCredential] if no Slack token is configured
25
+ # @raise [Ask::Auth::InvalidCredential] if the token is rejected
26
+ def self.client
27
+ token = Ask::Auth.resolve(:slack_token)
28
+
29
+ ClientProxy.new(::Slack::Web::Client.new(token: token))
30
+ end
31
+
32
+ # Proxies method calls to a +::Slack::Web::Client+, converting authentication
33
+ # errors into +Ask::Auth::InvalidCredential+.
34
+ class ClientProxy < ::BasicObject
35
+ def initialize(client)
36
+ @client = client
37
+ end
38
+
39
+ def method_missing(name, ...)
40
+ @client.public_send(name, ...)
41
+ rescue ::Slack::Web::Api::Errors::NotAuthed,
42
+ ::Slack::Web::Api::Errors::InvalidAuth,
43
+ ::Slack::Web::Api::Errors::AccountInactive,
44
+ ::Slack::Web::Api::Errors::TokenRevoked,
45
+ ::Slack::Web::Api::Errors::TokenExpired
46
+ ::Kernel.raise ::Ask::Auth::InvalidCredential, :slack_token
47
+ end
48
+
49
+ def respond_to_missing?(name, include_private = false)
50
+ @client.respond_to?(name, include_private) || super
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Slack
5
+ # Human-readable description of the Slack service context.
6
+ DESCRIPTION = "Slack — messaging, channels, files, search, workspace management"
7
+
8
+ # Base URL for Slack Web API methods.
9
+ DOCS_URL = "https://api.slack.com/methods"
10
+
11
+ # URL for the Slack OpenAPI specification.
12
+ OPENAPI_URL = "https://api.slack.com/specs/openapi"
13
+
14
+ # Credential name used with Ask::Auth.resolve.
15
+ AUTH_NAME = :slack_token
16
+
17
+ # Instructions for obtaining a Slack Bot User OAuth Token.
18
+ AUTH_HOW = "Create a Slack app at https://api.slack.com/apps — get a Bot User OAuth Token (xoxb-). Scopes: chat:write, channels:read, users:read, files:read"
19
+
20
+ # Gem name for the Slack API client.
21
+ GEM_NAME = "slack-ruby-client"
22
+
23
+ # Required gem version constraint.
24
+ GEM_VERSION = "~> 3.1"
25
+
26
+ # URL for slack-ruby-client library documentation.
27
+ GEM_DOCS = "https://rubydoc.info/gems/slack-ruby-client"
28
+
29
+ # Quick-start Ruby code snippet for agents to copy-paste.
30
+ QUICK_START = <<~RUBY
31
+ client = Ask::Slack.client
32
+ client.channels_list
33
+ client.conversations_list
34
+ client.chat_postMessage(channel: "#general", text: "Hello from ask-rb!")
35
+ client.users_list
36
+ client.conversations_history(channel: "C123456")
37
+ RUBY
38
+ end
39
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Slack
5
+ # Structured error knowledge for AI agents working with the Slack Web API.
6
+ #
7
+ # Provides human-readable guidance for common HTTP status codes, rate
8
+ # limiting, pagination, and authentication errors encountered when
9
+ # using the slack-ruby-client.
10
+ module Errors
11
+ # Rate limit information.
12
+ #
13
+ # Slack rate limits are applied per-method, per-app. Limits vary
14
+ # by endpoint. When rate-limited, +Slack::Web::Api::Errors::TooManyRequestsError+
15
+ # is raised with a +Retry-After+ header.
16
+ RATE_LIMIT = {
17
+ typical_limit: "Varies per method (typically 1–50 requests per minute per app)",
18
+ error_class: "Slack::Web::Api::Errors::TooManyRequestsError",
19
+ retry_header: "Retry-After (seconds)",
20
+ action: "Wait for the Retry-After duration, then retry the request. Use the retry_after method on the error."
21
+ }.freeze
22
+
23
+ # Common error strings returned by the Slack Web API and how to handle them.
24
+ ERRORS = {
25
+ "not_authed" => {
26
+ message: "No authentication token provided.",
27
+ action: "Ensure Ask::Slack.client is called with a valid token. Check that SLACK_TOKEN is set."
28
+ },
29
+ "invalid_auth" => {
30
+ message: "The authentication token is invalid or malformed.",
31
+ action: "Verify your Slack token is correct. It should start with xoxb- (bot) or xoxp- (user)."
32
+ },
33
+ "account_inactive" => {
34
+ message: "The authentication token is for a deleted or disabled account.",
35
+ action: "Reactivate the Slack app or generate a new token at https://api.slack.com/apps."
36
+ },
37
+ "token_revoked" => {
38
+ message: "The authentication token has been revoked.",
39
+ action: "Generate a new token at https://api.slack.com/apps and update your credentials."
40
+ },
41
+ "token_expired" => {
42
+ message: "The authentication token has expired.",
43
+ action: "Refresh the token or generate a new one at https://api.slack.com/apps."
44
+ },
45
+ "token_not_found" => {
46
+ message: "The authentication token could not be found.",
47
+ action: "Check that the token has been properly set via Ask::Auth."
48
+ },
49
+ "rate_limited" => {
50
+ message: "Slack API rate limit exceeded.",
51
+ action: "Check the Retry-After header, wait the specified duration, then retry. Use Slack::Web::Api::Errors::TooManyRequestsError#retry_after."
52
+ },
53
+ "ratelimited" => {
54
+ message: "Slack API rate limit exceeded (alternative error string).",
55
+ action: "Same as 'rate_limited' — wait for the Retry-After duration, then retry."
56
+ },
57
+ "channel_not_found" => {
58
+ message: "The specified channel was not found.",
59
+ action: "Verify the channel ID (starts with C) or channel name. Fetch available channels with client.conversations_list."
60
+ },
61
+ "user_not_found" => {
62
+ message: "The specified user was not found.",
63
+ action: "Verify the user ID (starts with U) or email. Fetch available users with client.users_list."
64
+ },
65
+ "not_in_channel" => {
66
+ message: "The bot user is not a member of the specified channel.",
67
+ action: "Invite the bot to the channel or use client.conversations_join to join public channels."
68
+ },
69
+ "restricted_action" => {
70
+ message: "The token lacks the required scopes for this action.",
71
+ action: "Add the required OAuth scope to your Slack app at https://api.slack.com/apps and reinstall."
72
+ },
73
+ "missing_scope" => {
74
+ message: "The token is missing a required OAuth scope.",
75
+ action: "Add the required scope at https://api.slack.com/apps, then reinstall the app to your workspace."
76
+ },
77
+ "invalid_arguments" => {
78
+ message: "The request contained invalid arguments.",
79
+ action: "Check the required parameters and data types for the method. See https://api.slack.com/methods for details."
80
+ },
81
+ "internal_error" => {
82
+ message: "Slack encountered an internal server error.",
83
+ action: "Retry with exponential backoff. If the issue persists, check https://status.slack.com."
84
+ },
85
+ "service_unavailable" => {
86
+ message: "Slack service is temporarily unavailable.",
87
+ action: "Retry with backoff. Check https://status.slack.com for ongoing incidents."
88
+ },
89
+ "fatal_error" => {
90
+ message: "Slack encountered a fatal error.",
91
+ action: "Retry with exponential backoff. Contact Slack support if the issue persists."
92
+ },
93
+ "deprecated_endpoint" => {
94
+ message: "The API endpoint has been deprecated.",
95
+ action: "Check https://api.slack.com/changelog for the replacement endpoint."
96
+ }
97
+ }.freeze
98
+
99
+ # HTTP status codes commonly returned by the Slack API.
100
+ STATUS_CODES = {
101
+ 200 => "OK — Request succeeded.",
102
+ 201 => "Created — File or resource was created.",
103
+ 204 => "No Content — Request succeeded, no response body.",
104
+ 400 => "Bad Request — Invalid parameters. Check the error field in the response.",
105
+ 401 => "Unauthorized — Token missing or invalid. Re-authenticate.",
106
+ 403 => "Forbidden — Token lacks required scopes.",
107
+ 404 => "Not Found — Resource does not exist.",
108
+ 429 => "Too Many Requests — Rate limit exceeded. Use Retry-After header.",
109
+ 500 => "Internal Server Error — Slack server issue. Retry with backoff.",
110
+ 502 => "Bad Gateway — Slack upstream issue. Retry with backoff.",
111
+ 503 => "Service Unavailable — Slack is down for maintenance. Retry later."
112
+ }.freeze
113
+
114
+ # Pagination guidance for large result sets.
115
+ PAGINATION = {
116
+ cursor_based: "Slack uses cursor-based pagination via the 'next_cursor' field in response_metadata.",
117
+ limit: "Use the 'limit' parameter to set page size (max 200 for most endpoints).",
118
+ pattern: "Set cursor parameter to response_metadata.next_cursor from the previous response.",
119
+ max_results: "For large result sets, iterate through all cursors."
120
+ }.freeze
121
+
122
+ # Map of Slack error strings to common exception classes.
123
+ EXCEPTION_CLASSES = {
124
+ "not_authed" => "Slack::Web::Api::Errors::NotAuthed",
125
+ "invalid_auth" => "Slack::Web::Api::Errors::InvalidAuth",
126
+ "account_inactive" => "Slack::Web::Api::Errors::AccountInactive",
127
+ "token_revoked" => "Slack::Web::Api::Errors::TokenRevoked",
128
+ "token_expired" => "Slack::Web::Api::Errors::TokenExpired",
129
+ "rate_limited" => "Slack::Web::Api::Errors::TooManyRequestsError",
130
+ "channel_not_found" => "Slack::Web::Api::Errors::ChannelNotFound",
131
+ "user_not_found" => "Slack::Web::Api::Errors::UserNotFound"
132
+ }.freeze
133
+
134
+ # Look up guidance for a Slack error string.
135
+ #
136
+ # @param error_string [String] The Slack API error string (e.g., "channel_not_found")
137
+ # @return [Hash, nil] A hash with +:message+ and +:action+ keys, or nil if unknown
138
+ def self.for(error_string)
139
+ ERRORS[error_string]
140
+ end
141
+
142
+ # Describe an HTTP status code.
143
+ #
144
+ # @param code [Integer] HTTP status code
145
+ # @return [String, nil] Description of the status code
146
+ def self.status_code_description(code)
147
+ STATUS_CODES[code]
148
+ end
149
+
150
+ # Look up the exception class name for a Slack error string.
151
+ #
152
+ # @param error_string [String] The Slack API error string
153
+ # @return [String, nil] The fully qualified exception class name
154
+ def self.exception_class(error_string)
155
+ EXCEPTION_CLASSES[error_string]
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Slack
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
data/lib/ask-slack.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/slack/version"
4
+ require_relative "ask/slack/context"
5
+ require_relative "ask/slack/client"
6
+ require_relative "ask/slack/error_guide"
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-slack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ask-auth
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: slack-ruby-client
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.25'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.25'
54
+ - !ruby/object:Gem::Dependency
55
+ name: mocha
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.1'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ description: Provides authenticated Slack client helper, context metadata, and error
83
+ guide for AI agents.
84
+ email:
85
+ - kaka@myrrlabs.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - lib/ask-slack.rb
93
+ - lib/ask/slack/client.rb
94
+ - lib/ask/slack/context.rb
95
+ - lib/ask/slack/error_guide.rb
96
+ - lib/ask/slack/version.rb
97
+ homepage: https://github.com/ask-rb/ask-slack
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '3.2'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 4.0.3
116
+ specification_version: 4
117
+ summary: Slack service context for the ask-rb ecosystem
118
+ test_files: []