Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b1596e12e | |||
| f3ad798d0e | |||
| ec5fc33af1 | |||
| 9e112ec3f9 | |||
| 3823aee4cf | |||
| b95391b03b | |||
| 56be11a950 | |||
| f6f4252075 |
+2
-1
@@ -40,4 +40,5 @@ Carthage/Build
|
||||
# Swift Package Manager
|
||||
.swiftpm/
|
||||
|
||||
*.resolved
|
||||
*.resolved
|
||||
.vscode
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
5.2.4
|
||||
5.5.2
|
||||
+7
-6
@@ -5,7 +5,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "SlackKit",
|
||||
platforms: [
|
||||
.macOS(.v10_10), .iOS(.v10), .tvOS(.v10)
|
||||
.macOS(.v10_15), .iOS(.v10), .tvOS(.v10)
|
||||
],
|
||||
products: [
|
||||
.library(name: "SlackKit", targets: ["SlackKit"]),
|
||||
@@ -16,9 +16,9 @@ let package = Package(
|
||||
.library(name: "SKWebAPI", targets: ["SKWebAPI"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Swifter", url: "https://github.com/httpswift/swifter.git", .upToNextMinor(from: "1.5.0")),
|
||||
.package(name: "WebSocket", url: "https://github.com/vapor/websocket", .upToNextMinor(from: "1.1.2")),
|
||||
.package(name: "Starscream", url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "4.0.4"))
|
||||
.package(url: "https://github.com/httpswift/swifter", from: .init(1, 5, 0)),
|
||||
.package(url: "https://github.com/vapor/websocket-kit", from: .init(2, 5, 0)),
|
||||
.package(url: "https://github.com/daltoniam/Starscream", from: .init(4, 0, 4)),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "SlackKit",
|
||||
@@ -34,11 +34,12 @@ let package = Package(
|
||||
"SKCore",
|
||||
"SKWebAPI",
|
||||
.product(name: "Starscream", package: "Starscream", condition: .when(platforms: [.macOS, .iOS, .tvOS])),
|
||||
.product(name: "WebSocket", package: "WebSocket", condition: .when(platforms: [.macOS, .linux])),
|
||||
.product(name: "WebSocketKit", package: "websocket-kit", condition: .when(platforms: [.macOS, .linux])),
|
||||
],
|
||||
path: "SKRTMAPI/Sources"),
|
||||
.target(name: "SKServer",
|
||||
dependencies: ["SKCore", "SKWebAPI", "Swifter"],
|
||||
dependencies: ["SKCore", "SKWebAPI",
|
||||
.product(name: "Swifter", package: "swifter")],
|
||||
path: "SKServer/Sources"),
|
||||
.target(name: "SKWebAPI",
|
||||
dependencies: ["SKCore"],
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
<p align="center"><img src="https://cloud.githubusercontent.com/assets/8311605/24083714/e921a0d4-0cb2-11e7-8384-d42113ef5056.png" alt="SlackKit" width="500"/></p>
|
||||
# **This library is no longer under active development.**
|
||||
|
||||
[](https://dev.azure.com/pzignego/SlackKit/_build/latest?definitionId=2&branchName=master)
|
||||
|
||||

|
||||

|
||||

|
||||
[](https://github.com/apple/swift-package-manager)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://cocoapods.org)
|
||||
|
||||
## SlackKit: Slack Apps in Swift
|
||||
### Description
|
||||
|
||||
SlackKit makes it easy to build Slack apps in Swift.
|
||||
|
||||
It's intended to expose all of the functionality of Slack's [Real Time Messaging API](https://api.slack.com/rtm) as well as the [web APIs](https://api.slack.com/web) that are accessible to [bot users](https://api.slack.com/bot-users). SlackKit also supports Slack’s [OAuth 2.0](https://api.slack.com/docs/oauth) flow including the [Add to Slack](https://api.slack.com/docs/slack-button) and [Sign in with Slack](https://api.slack.com/docs/sign-in-with-slack) buttons, [incoming webhooks](https://api.slack.com/incoming-webhooks), [slash commands](https://api.slack.com/slash-commands), and [message buttons](https://api.slack.com/docs/message-buttons).
|
||||
## SlackKit
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -150,11 +136,3 @@ Don’t need the whole banana? Want more control over the low-level implementati
|
||||
|
||||
### Examples
|
||||
You can find the source code for several example applications [here](https://github.com/pvzig/SlackKit/tree/master/Examples).
|
||||
|
||||
### Tutorials
|
||||
- [Build a Slack Bot and Deploy to Heroku](https://medium.com/@pvzig/building-slack-bots-in-swift-b99e243e444c)
|
||||
|
||||
### Get In Touch
|
||||
Twitter: [@pvzig](https://twitter.com/pvzig)
|
||||
|
||||
Email: <peter@launchsoft.co>
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
#if os(Linux) || os(macOS) && !COCOAPODS
|
||||
import Foundation
|
||||
import HTTP
|
||||
import WebSocket
|
||||
import NIO
|
||||
import WebSocketKit
|
||||
|
||||
// Builds with *Swift Package Manager ONLY*
|
||||
public class VaporEngineRTM: RTMWebSocket {
|
||||
@@ -34,7 +34,7 @@ public class VaporEngineRTM: RTMWebSocket {
|
||||
public weak var delegate: RTMDelegate?
|
||||
// Websocket
|
||||
private var websocket: WebSocket?
|
||||
private var futureWebsocket: Future<WebSocket>?
|
||||
private var futureWebsocket: EventLoopFuture<Void>?
|
||||
|
||||
public required init() {}
|
||||
|
||||
@@ -43,14 +43,10 @@ public class VaporEngineRTM: RTMWebSocket {
|
||||
fatalError("ERROR - Cannot extract host from '\(url.absoluteString)'")
|
||||
}
|
||||
|
||||
let scheme: HTTPScheme = url.scheme == "wss" ? .wss : .ws
|
||||
futureWebsocket = HTTPClient.webSocket(
|
||||
scheme: scheme,
|
||||
hostname: host,
|
||||
path: url.path,
|
||||
on: eventLoopGroup
|
||||
)
|
||||
.do(didConnect)
|
||||
futureWebsocket = WebSocketClient(eventLoopGroupProvider: .shared(eventLoopGroup))
|
||||
.connect(scheme: url.scheme ?? "ws", host: host, port: 8080, path: url.path, query: nil, headers: [:]) { [weak self] webSocket in
|
||||
self?.didConnect(websocket: webSocket)
|
||||
}
|
||||
}
|
||||
|
||||
func didConnect(websocket: WebSocket) {
|
||||
@@ -62,17 +58,14 @@ public class VaporEngineRTM: RTMWebSocket {
|
||||
self.delegate?.receivedMessage(text)
|
||||
}
|
||||
|
||||
websocket.onError { ws, error in
|
||||
|
||||
}
|
||||
|
||||
websocket.onCloseCode { closeCode in
|
||||
self.delegate?.disconnected()
|
||||
}
|
||||
websocket.onClose
|
||||
.whenComplete { [weak self] _ in
|
||||
self?.delegate?.disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
websocket?.close()
|
||||
_ = websocket?.close()
|
||||
websocket = nil
|
||||
futureWebsocket = nil
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ extension HttpRequest {
|
||||
return try! Request(
|
||||
method: HTTPMethod.custom(named: method),
|
||||
path: path,
|
||||
queryPairs: queryParams,
|
||||
body: String(bytes: body, encoding: .utf8) ?? "",
|
||||
headers: HTTPHeaders(headers: headers.map ({ Header(name: $0.key, value: $0.value) }))
|
||||
)
|
||||
|
||||
@@ -17,8 +17,10 @@ import Foundation
|
||||
public protocol RequestType {
|
||||
/// The HTTP request body.
|
||||
var body: Data { get }
|
||||
/// The HTTP request path, including query string and any fragments.
|
||||
/// The HTTP request path
|
||||
var path: String { get }
|
||||
/// The HTTP request query pairs
|
||||
var queryPairs: [(String, String)] { get }
|
||||
/// The HTTP request method.
|
||||
var method: HTTPMethod { get }
|
||||
/// The HTTP request headers.
|
||||
@@ -30,14 +32,16 @@ public struct Request: RequestType {
|
||||
|
||||
public var method: HTTPMethod
|
||||
public var path: String
|
||||
public var queryPairs: [(String, String)]
|
||||
public var body: Data
|
||||
public var headers: HTTPHeaders
|
||||
|
||||
/// Create a Request
|
||||
/// Throws an error if the body parameter cannot be converted to Data
|
||||
public init(method: HTTPMethod, path: String, body: String, headers: HTTPHeaders) throws {
|
||||
public init(method: HTTPMethod, path: String, queryPairs: [(String, String)], body: String, headers: HTTPHeaders) throws {
|
||||
self.method = method
|
||||
self.path = path
|
||||
self.queryPairs = queryPairs
|
||||
guard let data = body.data(using: .utf8) else {
|
||||
throw TitanError.dataConversion
|
||||
}
|
||||
@@ -46,9 +50,10 @@ public struct Request: RequestType {
|
||||
}
|
||||
|
||||
/// Create a Request
|
||||
public init(method: HTTPMethod, path: String, body: Data, headers: HTTPHeaders) {
|
||||
public init(method: HTTPMethod, path: String, queryPairs: [(String, String)], body: Data, headers: HTTPHeaders) {
|
||||
self.method = method
|
||||
self.path = path
|
||||
self.queryPairs = queryPairs
|
||||
self.body = body
|
||||
self.headers = headers
|
||||
}
|
||||
@@ -57,7 +62,7 @@ public struct Request: RequestType {
|
||||
extension Request {
|
||||
/// Create a Request from a RequestType
|
||||
public init(request: RequestType) {
|
||||
self.init(method: request.method, path: request.path, body: request.body, headers: request.headers)
|
||||
self.init(method: request.method, path: request.path, queryPairs: request.queryPairs, body: request.body, headers: request.headers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,43 +14,6 @@
|
||||
import Foundation
|
||||
|
||||
public extension RequestType {
|
||||
/// The pairs of keys and values in the query string of the `RequestType`s path.
|
||||
/// Complexity: 0(n) on all invocations.
|
||||
var queryPairs: [(key: String, value: String)] {
|
||||
// Ensure there is a query string, otherwise return
|
||||
guard let indexOfQuery = self.path.firstIndex(of: "?") else {
|
||||
return []
|
||||
}
|
||||
|
||||
let query = self.path.suffix(from: indexOfQuery).dropFirst()
|
||||
// Create an array of the individual query pairs, e.g. ["foo=bar", "baz=qux"]
|
||||
let pairs = query.split(separator: "&")
|
||||
|
||||
// Decode `foo=bar` -> `(key: "foo", value: "bar")`, percent decoding any values along the way
|
||||
return pairs.map { pair -> (key: String, value: String) in
|
||||
// Separate the query pair into an array, e.g. "foo=bar" -> ["foo", "bar"]
|
||||
let comps = pair.split(separator: "=")
|
||||
// Split returns an array of subsequences which should conform to StringProtocol, however on Linux StringProtocol is out of date.
|
||||
// Workaround for https://bugs.swift.org/browse/SR-5727 by converting to a String directly
|
||||
.map(String.init)
|
||||
.map {
|
||||
// Percent encoding mandates that "%20" = <space>"
|
||||
// however, many applications use "+" to mean space as well, so decode those (before we decode any percent-encoded plus signs!)
|
||||
return $0.replacingOccurrences(of: "+", with: " ")
|
||||
}.map {
|
||||
return $0.removingPercentEncoding ?? ""
|
||||
}
|
||||
switch comps.count {
|
||||
case 1: // "?foo="
|
||||
return (key: String(comps[0]), value: "")
|
||||
case 2: // "?foo=bar"
|
||||
return (key: String(comps[0]), value: String(comps[1]))
|
||||
default: // "?"
|
||||
return (key: "", value: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the query string as a dictionary, with case sensitive keys.
|
||||
/// Complexity: 0(n) on all invocations.
|
||||
var query: [String: String] {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
import Foundation
|
||||
|
||||
extension Request {
|
||||
public init(_ method: HTTPMethod = .get, _ path: String = "/", _ body: String = "", _ headers: HTTPHeaders = HTTPHeaders()) {
|
||||
self.init(method: method, path: path, body: body.data(using: .utf8) ?? Data(), headers: headers)
|
||||
public init(_ method: HTTPMethod = .get, _ path: String = "/", _ queryPairs: [(String, String)] = [], _ body: String = "", _ headers: HTTPHeaders = HTTPHeaders()) {
|
||||
self.init(method: method, path: path, queryPairs: queryPairs, body: body.data(using: .utf8) ?? Data(), headers: headers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public enum Endpoint: String {
|
||||
case mpimList = "mpim.list"
|
||||
case mpimMark = "mpim.mark"
|
||||
case mpimOpen = "mpim.open"
|
||||
case oauthAccess = "oauth.v2.access"
|
||||
case oauthAccess = "oauth.access"
|
||||
case pinsAdd = "pins.add"
|
||||
case pinsList = "pins.list"
|
||||
case pinsRemove = "pins.remove"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SlackKit"
|
||||
s.version = "4.6.0"
|
||||
s.version = "4.7.0"
|
||||
s.summary = "Write Slack apps in Swift"
|
||||
s.homepage = "https://github.com/pvzig/SlackKit"
|
||||
s.license = "MIT"
|
||||
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
||||
s.source = { :git => "https://github.com/pvzig/SlackKit.git", :tag => s.version.to_s }
|
||||
s.social_media_url = "https://twitter.com/pvzig"
|
||||
s.platforms = { :ios => '10.0', :osx => '10.11', :tvos => '10.0' }
|
||||
s.swift_version = '5.2.4'
|
||||
s.swift_version = '5.5.2'
|
||||
s.cocoapods_version = '>= 1.4.0'
|
||||
s.default_subspec = "SlackKit"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user