Compare commits

...

12 Commits

Author SHA1 Message Date
Peter Zignego 58bade8ec9 Bump version 2017-01-04 22:10:18 -05:00
Peter Zignego 7d7f5c40d6 Update readme 2017-01-04 22:09:18 -05:00
Peter Zignego c9fd54d106 Merge pull request #66 from pvzig/3.1.7
3.1.7
2017-01-04 22:08:10 -05:00
Peter Zignego 2f1ce799ad URLComponents and WebAPI clean up 2017-01-04 22:06:47 -05:00
Peter Zignego 7f02f4cf99 Update readme 2017-01-03 22:07:57 -05:00
Peter Zignego a9066c0d0a Bump version 2017-01-03 21:29:49 -05:00
Peter Zignego 29739dba74 Merge pull request #65 from pvzig/minor-updates
Code styling
2017-01-03 21:20:00 -05:00
Peter Zignego d60a7094a4 Readme 2017-01-03 21:18:58 -05:00
Peter Zignego 7edd4210f6 Code quality improvements 2017-01-02 22:41:03 -05:00
Peter Zignego 2abaecbd14 Lowercase enums 2017-01-02 22:39:56 -05:00
Peter Zignego 346499e03b Update podspec version 2016-11-25 12:46:48 -05:00
Peter Zignego bc72a52bf5 Update readme 2016-11-24 12:37:48 -05:00
17 changed files with 467 additions and 557 deletions
+5 -9
View File
@@ -1,6 +1,6 @@
![SlackKit](https://cloud.githubusercontent.com/assets/8311605/10260893/5ec60f96-694e-11e5-91fd-da6845942201.png)
![Swift Version](https://img.shields.io/badge/Swift-3.0-orange.svg) ![Plaforms](https://img.shields.io/badge/Platforms-macOS,iOS,tvOS-lightgrey.svg) ![License MIT](https://img.shields.io/badge/License-MIT-lightgrey.svg) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage)
![Swift Version](https://img.shields.io/badge/Swift-3.0-orange.svg) ![Plaforms](https://img.shields.io/badge/Platforms-macOS,iOS,tvOS-lightgrey.svg) ![License MIT](https://img.shields.io/badge/License-MIT-lightgrey.svg) [![CocoaPods compatible](https://img.shields.io/badge/CocoaPods-compatible-brightgreen.svg)](https://cocoapods.org) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
## SlackKit: A Swift Slack Client Library
### Description
@@ -18,11 +18,11 @@ To build the SlackKit project directly, first build the dependencies using Carth
Add SlackKit to your pod file:
```
use_frameworks!
pod 'SlackKit', '~> 3.1.2'
pod 'SlackKit', '~> 3.1.7'
```
and run
```
# Use CocoaPods version >= 1.1.0.rc.2 (gem install cocoapods --pre)
# Use CocoaPods version >= 1.1.0
pod install
```
@@ -36,10 +36,6 @@ and run
```
carthage bootstrap
```
**Note:** SlackKit currently takes a _long_ time for the compiler to compile with optimizations turned on. I'm currently exploring a potential fix for this issue. In the meantime, you may want to skip the waiting and build it in the debug configuration instead:
```
carthage bootstrap --configuration "Debug"
```
Drag the built `SlackKit.framework` into your Xcode project.
@@ -88,7 +84,7 @@ incoming.postMessage(message)
#### Slash Commands
After [configuring your slash command in Slack](https://my.slack.com/services/new/slash-commands) (you can also provide slash commands as part of a [Slack App](https://api.slack.com/slack-apps)), initialize a webhook server with the token for the slash command, a configured route, and a response.
```swift
let response = Response(text: "Hello, World!", responseType: .InChannel)
let response = Response(text: "Hello, World!", responseType: .inChannel)
let webhook = WebhookServer(token: "SLASH-COMMAND-TOKEN", route: "hello_world", response: response)
webhook.start()
```
@@ -111,7 +107,7 @@ let attachment = Attachment(fallback: "Hello World Attachment", title: "Attachme
To act on message actions, initialize an instance of the `MessageActionServer` using your apps verification token, your specified interactive messages request URL route, and a `MessageActionResponder`:
```swift
let action = Action(name: "hello_world", text: "Hello, World!")
let response = Response(text: "Hello, 🌎!", responseType: .InChannel)
let response = Response(text: "Hello, 🌎!", responseType: .inChannel)
let responder = MessageActionResponder(responses: [(action, response)])
let server = MessageActionServer(token: "SLACK-APP-VERIFICATION-TOKEN", route: "actions", responder: responder)
server.start()
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "3.1.2"
s.version = "3.1.7"
s.summary = "a Slack client library for OS X, iOS, and tvOS written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
+6 -6
View File
@@ -39,7 +39,7 @@ public struct Action {
confirm = Confirm(confirm:action?["confirm"] as? [String: Any])
}
public init(name: String, text: String, style: ActionStyle = .Default, value: String? = nil, confirm: Confirm? = nil) {
public init(name: String, text: String, style: ActionStyle = .defaultStyle, value: String? = nil, confirm: Confirm? = nil) {
self.type = "button"
self.name = name
self.text = text
@@ -92,12 +92,12 @@ public struct Action {
}
public enum ActionStyle: String {
case Default = "default"
case Primary = "primary"
case Danger = "danger"
case defaultStyle = "default"
case primary = "primary"
case danger = "danger"
}
public enum ResponseType: String {
case InChannel = "in_channel"
case Ephemeral = "ephemeral"
case inChannel = "in_channel"
case ephemeral = "ephemeral"
}
+3 -3
View File
@@ -109,7 +109,7 @@ public struct Attachment {
}
public enum AttachmentColor: String {
case Good = "good"
case Warning = "warning"
case Danger = "danger"
case good = "good"
case warning = "warning"
case danger = "danger"
}
+1 -1
View File
@@ -49,7 +49,7 @@ public extension Client {
if let channel = channel {
success(channel.0)
} else {
webAPI.openIM(id, success: success, failure: failure)
webAPI.openIM(userID: id, success: success, failure: failure)
}
}
+13 -13
View File
@@ -44,7 +44,7 @@ public final class Client: WebSocketDelegate {
}
internal var webSocket: WebSocket?
fileprivate let pingPongQueue = DispatchQueue(label: "com.launchsoft.SlackKit")
private let pingPongQueue = DispatchQueue(label: "com.launchsoft.SlackKit")
internal var ping: Double?
internal var pong: Double?
internal var options: ClientOptions?
@@ -75,7 +75,7 @@ public final class Client: WebSocketDelegate {
public func connect(options: ClientOptions = ClientOptions()) {
self.options = options
webAPI.rtmStart(options.simpleLatest, noUnreads: options.noUnreads, mpimAware: options.mpimAware, success: {(response) in
webAPI.rtmStart(simpleLatest: options.simpleLatest, noUnreads: options.noUnreads, mpimAware: options.mpimAware, success: {(response) in
guard let socketURL = response["url"] as? String, let url = URL(string: socketURL) else {
return
}
@@ -101,7 +101,7 @@ public final class Client: WebSocketDelegate {
}
}
fileprivate func format(message: String, channel: String) throws -> Data {
private func format(message: String, channel: String) throws -> Data {
let json: [String: Any] = [
"id": Date().slackTimestamp,
"type": "message",
@@ -112,7 +112,7 @@ public final class Client: WebSocketDelegate {
return try JSONSerialization.data(withJSONObject: json, options: [])
}
fileprivate func addSentMessage(_ dictionary: [String: Any]) {
private func addSentMessage(_ dictionary: [String: Any]) {
var message = dictionary
guard let id = message["id"] as? NSNumber else {
return
@@ -125,7 +125,7 @@ public final class Client: WebSocketDelegate {
}
//MARK: - RTM Ping
fileprivate func pingRTMServerAt(interval: TimeInterval) {
private func pingRTMServerAt(interval: TimeInterval) {
let delay = DispatchTime.now() + Double(Int64(interval * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
pingPongQueue.asyncAfter(deadline: delay, execute: {
guard self.connected && self.timeoutCheck() else {
@@ -137,7 +137,7 @@ public final class Client: WebSocketDelegate {
})
}
fileprivate func sendRTMPing() {
private func sendRTMPing() {
guard connected else {
return
}
@@ -154,7 +154,7 @@ public final class Client: WebSocketDelegate {
}
}
fileprivate func timeoutCheck() -> Bool {
private func timeoutCheck() -> Bool {
if let pong = pong, let ping = ping, let timeout = options?.timeout {
if pong - ping < timeout {
return true
@@ -168,7 +168,7 @@ public final class Client: WebSocketDelegate {
}
//MARK: - Client setup
fileprivate func initialSetup(JSON: [String: Any]) {
private func initialSetup(JSON: [String: Any]) {
team = Team(team: JSON["team"] as? [String: Any])
authenticatedUser = User(user: JSON["self"] as? [String: Any])
authenticatedUser?.doNotDisturbStatus = DoNotDisturbStatus(status: JSON["dnd"] as? [String: Any])
@@ -181,28 +181,28 @@ public final class Client: WebSocketDelegate {
enumerateSubteams(JSON["subteams"] as? [String: Any])
}
fileprivate func addUser(_ aUser: [String: Any]) {
private func addUser(_ aUser: [String: Any]) {
let user = User(user: aUser)
if let id = user.id {
users[id] = user
}
}
fileprivate func addChannel(_ aChannel: [String: Any]) {
private func addChannel(_ aChannel: [String: Any]) {
let channel = Channel(channel: aChannel)
if let id = channel.id {
channels[id] = channel
}
}
fileprivate func addBot(_ aBot: [String: Any]) {
private func addBot(_ aBot: [String: Any]) {
let bot = Bot(bot: aBot)
if let id = bot.id {
bots[id] = bot
}
}
fileprivate func enumerateSubteams(_ subteams: [String: Any]?) {
private func enumerateSubteams(_ subteams: [String: Any]?) {
if let subteams = subteams {
if let all = subteams["all"] as? [[String: Any]] {
for item in all {
@@ -222,7 +222,7 @@ public final class Client: WebSocketDelegate {
}
// MARK: - Utilities
fileprivate func enumerateObjects(_ array: [Any]?, initalizer: ([String: Any])-> Void) {
private func enumerateObjects(_ array: [Any]?, initalizer: ([String: Any])-> Void) {
if let array = array {
for object in array {
if let dictionary = object as? [String: Any] {
-2
View File
@@ -22,7 +22,6 @@
// THE SOFTWARE.
internal enum EventType: String {
case hello = "hello"
case message = "message"
case userTyping = "user_typing"
@@ -97,7 +96,6 @@ internal enum EventType: String {
}
internal enum MessageSubtype: String {
case botMessage = "bot_message"
case meMessage = "me_message"
case messageChanged = "message_changed"
-16
View File
@@ -39,19 +39,3 @@ internal extension String {
return escapedString
}
}
internal extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any {
var requestStringFromParameters: String {
var requestString = ""
for key in self.keys {
if let value = self[key] as? String, let encodedValue = value.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) {
requestString += "&\(key)=\(encodedValue)"
} else if let value = self[key] {
requestString += "&\(key)=\(value)"
}
}
return requestString
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ public struct IncomingWebhook {
}
}
fileprivate func jsonBody(_ response: [String: Any]) -> [String: Any] {
private func jsonBody(_ response: [String: Any]) -> [String: Any] {
var json = response
json["channel"] = channel
json["username"] = username
+30 -25
View File
@@ -27,22 +27,18 @@ internal struct NetworkInterface {
private let apiUrl = "https://slack.com/api/"
internal func request(_ endpoint: Endpoint, token: String? = nil, parameters: [String: Any]?, successClosure: @escaping ([String: Any])->Void, errorClosure: @escaping (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?"
if let token = token {
requestString += "token=\(token)"
internal func request(_ endpoint: Endpoint, parameters: [String: Any?], successClosure: @escaping ([String: Any])->Void, errorClosure: @escaping (SlackError)->Void) {
var components = URLComponents(string: "\(apiUrl)\(endpoint.rawValue)")
if parameters.count > 0 {
components?.queryItems = filterNilParameters(parameters).map { URLQueryItem(name: $0.0, value: "\($0.1)") }
}
if let params = parameters {
requestString += params.requestStringFromParameters
}
guard let url = URL(string: requestString) else {
guard let url = components?.url else {
errorClosure(SlackError.clientNetworkError)
return
}
let request = URLRequest(url:url)
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {(data, response, internalError) in
do {
successClosure(try self.handleResponse(data, response: response, internalError: internalError))
} catch let error {
@@ -62,8 +58,7 @@ internal struct NetworkInterface {
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.httpBody = data
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
URLSession.shared.dataTask(with: request) {(data, response, internalError) in
if internalError == nil {
success(true)
} else {
@@ -72,12 +67,12 @@ internal struct NetworkInterface {
}.resume()
}
internal func uploadRequest(_ token: String, data: Data, parameters: [String: Any]?, successClosure: @escaping ([String: Any])->Void, errorClosure: @escaping (SlackError)->Void) {
var requestString = "\(apiUrl)\(Endpoint.filesUpload.rawValue)?token=\(token)"
if let params = parameters {
requestString = requestString + params.requestStringFromParameters
internal func uploadRequest(data: Data, parameters: [String: Any?], successClosure: @escaping ([String: Any])->Void, errorClosure: @escaping (SlackError)->Void) {
var components = URLComponents(string: "\(apiUrl)\(Endpoint.filesUpload.rawValue)")
if parameters.count > 0 {
components?.queryItems = filterNilParameters(parameters).map { URLQueryItem(name: $0.0, value: "\($0.1)") }
}
guard let url = URL(string: requestString) else {
guard let url = components?.url else {
errorClosure(SlackError.clientNetworkError)
return
}
@@ -87,9 +82,9 @@ internal struct NetworkInterface {
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let boundaryStart = "--\(boundaryConstant)\r\n"
let boundaryEnd = "--\(boundaryConstant)--\r\n"
let contentDispositionString = "Content-Disposition: form-data; name=\"file\"; filename=\"\(parameters!["filename"])\"\r\n"
let contentTypeString = "Content-Type: \(parameters!["filetype"])\r\n\r\n"
let contentDispositionString = "Content-Disposition: form-data; name=\"file\"; filename=\"\(parameters["filename"])\"\r\n"
let contentTypeString = "Content-Type: \(parameters["filetype"])\r\n\r\n"
var requestBodyData: Data = Data()
requestBodyData.append(boundaryStart.data(using: String.Encoding.utf8)!)
requestBodyData.append(contentDispositionString.data(using: String.Encoding.utf8)!)
@@ -100,9 +95,8 @@ internal struct NetworkInterface {
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.httpBody = requestBodyData as Data
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
URLSession.shared.dataTask(with: request) {(data, response, internalError) in
do {
successClosure(try self.handleResponse(data, response: response, internalError: internalError))
} catch let error {
@@ -148,4 +142,15 @@ internal struct NetworkInterface {
private func randomBoundary() -> String {
return String(format: "slackkit.boundary.%08x%08x", arc4random(), arc4random())
}
//MARK: - Filter Nil Parameters
private func filterNilParameters(_ parameters: [String: Any?]) -> [String: Any] {
var finalParameters = [String: Any]()
for (key, value) in parameters {
if let unwrapped = value {
finalParameters[key] = unwrapped
}
}
return finalParameters
}
}
+15 -15
View File
@@ -30,14 +30,14 @@ internal protocol OAuthDelegate {
public struct OAuthServer {
fileprivate let oauthURL = "https://slack.com/oauth/authorize"
fileprivate let http = HttpServer()
fileprivate let clientID: String
fileprivate let clientSecret: String
fileprivate let state: String?
fileprivate let redirectURI: String?
fileprivate var delegate: OAuthDelegate?
private let oauthURL = "https://slack.com/oauth/authorize"
private let http = HttpServer()
private let clientID: String
private let clientSecret: String
private let state: String?
private let redirectURI: String?
private var delegate: OAuthDelegate?
internal init(clientID: String, clientSecret: String, state: String? = nil, redirectURI: String? = nil, port:in_port_t = 8080, forceIPV4: Bool = false, delegate: OAuthDelegate? = nil) throws {
self.clientID = clientID
@@ -61,12 +61,12 @@ public struct OAuthServer {
http.stop()
}
fileprivate func oauthRoute() {
private func oauthRoute() {
http["/oauth"] = { request in
guard let response = AuthorizeResponse(queryParameters: request.queryParams), response.state == self.state else {
return .badRequest(.text("Bad request."))
}
WebAPI.oauthAccess(self.clientID, clientSecret: self.clientSecret, code: response.code, redirectURI: self.redirectURI, success: {(response) in
WebAPI.oauthAccess(clientID: self.clientID, clientSecret: self.clientSecret, code: response.code, redirectURI: self.redirectURI, success: {(response) in
self.delegate?.userAuthed(OAuthResponse(response: response))
}, failure: {(error) in
print("Authorization failed")
@@ -78,13 +78,13 @@ public struct OAuthServer {
}
}
fileprivate func oauthURLRequest(_ authorize: AuthorizeRequest) -> URLRequest? {
var requestString = "\(oauthURL)?client_id=\(authorize.clientID)"
requestString += authorize.parameters.requestStringFromParameters
guard let url = URL(string: requestString) else {
private func oauthURLRequest(_ authorize: AuthorizeRequest) -> URLRequest? {
var components = URLComponents(string: "\(oauthURL)")
components?.queryItems = [URLQueryItem(name: "client_id", value: "\(authorize.clientID)")]
guard let url = components?.url else {
return nil
}
return URLRequest(url:url)
return URLRequest(url: url)
}
public func authorizeRequest(_ scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil) -> URLRequest? {
+1 -1
View File
@@ -27,7 +27,7 @@ public final class SlackKit: OAuthDelegate {
internal(set) public var oauth: OAuthServer?
internal(set) public var clients: [String: Client] = [:]
fileprivate let clientOptions: ClientOptions
private let clientOptions: ClientOptions
// Initalization block
public var onClientInitalization: ((Client) -> Void)?
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -39,7 +39,7 @@ open class WebhookServer: Server {
}
}
fileprivate func replyForResponse(_ response: Response) -> Reply {
private func replyForResponse(_ response: Response) -> Reply {
if response.attachments == nil && response.responseType == nil {
return Reply.text(body: response.text)
} else {
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.5</string>
<string>3.1.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.5</string>
<string>3.1.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.5</string>
<string>3.1.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>