Compare commits

..

63 Commits

Author SHA1 Message Date
Peter Zignego b00bf0b2a3 Merge pull request #64 from pvzig/swift3
Update to Swift 3
2016-12-29 23:30:02 -05:00
Peter Zignego b877fac99b Prefer private to fileprivate 2016-12-29 23:27:10 -05:00
Peter Zignego f50107f261 Clean up web api 2016-12-29 23:23:57 -05:00
Peter Zignego 0274f5842e Add files.info 2016-12-29 23:22:58 -05:00
Peter Zignego a9708d001a Fix file uploads 2016-12-29 22:41:31 -05:00
Peter Zignego 465a3860ee Update examples 2016-12-29 20:52:33 -05:00
Peter Zignego 9bf2961dc7 Deadline updates 2016-12-29 20:22:56 -05:00
Peter Zignego d81f2ab221 RTM ping 2016-12-29 20:22:46 -05:00
Peter Zignego 337e3b7709 Typo fix 2016-12-28 22:18:05 -05:00
Peter Zignego f50efd5f7a Attachments fixes 2016-12-28 22:17:15 -05:00
Peter Zignego f80dcf4ae9 WebSocket ping 2016-12-28 21:11:10 -05:00
Peter Zignego 13b8b36c10 Linux build errors 2016-12-27 23:21:13 -05:00
Peter Zignego 696da2722b Move file to Sources 2016-12-27 23:16:47 -05:00
Peter Zignego 461115f6d3 Unknown event errors 2016-12-27 22:56:17 -05:00
Peter Zignego 38aaedee2a Network interface 2016-12-27 22:07:10 -05:00
Peter Zignego 98a3973ab0 Swift 3: Client updates 2016-12-25 12:50:32 -06:00
Peter Zignego 846bc84035 Slack 3: Web API 2016-12-24 10:42:36 -06:00
Peter Zignego 283e3c0614 Swift 3: Package update 2016-12-24 10:40:46 -06:00
Peter Zignego 44568d407c Swift 3: Model updates 2016-12-24 10:40:34 -06:00
Peter Zignego b8f20d7397 Update README.md 2016-09-12 23:54:07 -04:00
Peter Zignego 2770499879 Update README.md 2016-09-12 23:52:41 -04:00
Peter Zignego 38e64939f1 Add badges 2016-09-12 23:51:24 -04:00
Peter Zignego 71f066a6f8 Add echobot example 2016-06-21 23:31:15 -04:00
Peter Zignego 4e87bea2fa Update snapshot version 2016-06-16 15:15:34 -04:00
Peter Zignego 3d6516922f Swift verison and random fix 2016-06-16 14:25:49 -04:00
Peter Zignego 1b98af2b11 05-09 snapshot updates 2016-06-15 22:10:39 -04:00
Peter Zignego 750604a801 Update C7 version 2016-06-15 21:52:19 -04:00
Peter Zignego 47f5d040a2 Fix version 2016-06-15 21:49:34 -04:00
Peter Zignego 3c34557617 Update package 2016-06-15 21:46:07 -04:00
Peter Zignego d60b8bf88a Comment out disconnect functionality
Underlying Zewo/WebSocket bug
2016-05-17 21:36:05 -04:00
Peter Zignego f2f95b7a26 Fix typo 2016-05-17 21:11:13 -04:00
Peter Zignego c4b38dbc14 Update readme 2016-05-17 20:17:28 -04:00
Peter Zignego 7fffbc8dba Weak self 2016-05-17 15:49:57 -04:00
Peter Zignego ab39fda0f5 Use Venice for async 2016-05-17 14:41:18 -04:00
Peter Zignego 11e9f2e9ab Fix random boundary 2016-05-17 13:19:50 -04:00
Peter Zignego a93b4b7966 Code improvements 2016-05-17 11:51:54 -04:00
Peter Zignego b639fff78e Add examples 2016-05-09 09:15:38 -04:00
Peter Zignego 6075577ec5 Update readme 2016-05-09 00:39:48 -04:00
Peter Zignego 7a967b8f7d Remove legacy header files 2016-05-08 23:31:14 -04:00
Peter Zignego 5f53f89693 Handle Bools properly 2016-05-08 23:29:18 -04:00
Peter Zignego 30d701f6de Remove project file from scm 2016-05-08 00:05:50 -04:00
Peter Zignego 82e200f4cc Fix gitignore 2016-05-08 00:04:22 -04:00
Peter Zignego 6cca5c3956 Ignore project file 2016-05-08 00:02:34 -04:00
Peter Zignego 43f184a8ed Bug fixes 2016-05-08 00:00:22 -04:00
Peter Zignego c5ce209e54 Bump dependency versions 2016-05-07 16:38:05 -04:00
Peter Zignego fc336cd2ee Updates 2016-05-07 16:37:50 -04:00
Peter Zignego 7138cf3f4f Update readme 2016-05-05 15:01:15 -04:00
Peter Zignego 00b2605a37 Add public contains extension 2016-05-05 13:14:48 -04:00
Peter Zignego 2561e8ba48 Bug fix 2016-05-04 23:31:02 -04:00
Peter Zignego 00b0823dbd Percent encoding bug fix 2016-05-04 23:25:19 -04:00
Peter Zignego b88ef3638c Project file updates 2016-05-04 20:37:09 -04:00
Peter Zignego 1675aa82fb Use foundation for percent encoding for now 2016-05-04 15:04:25 -04:00
Peter Zignego 01be45e979 Fixes 2016-05-03 19:15:37 -04:00
Peter Zignego dc3336a807 Percent encoding fix 2016-05-03 00:41:28 -04:00
Peter Zignego 5962ce5115 Fixes 2016-05-02 21:22:47 -04:00
Peter Zignego db6bf52eaa Update dependencies 2016-05-02 17:57:06 -04:00
Peter Zignego 32ae1ed7a2 Remove foundation dependencies 2016-05-02 17:56:35 -04:00
Peter Zignego 2e4b4390b3 Zewo implementation 2016-05-02 13:40:53 -04:00
Peter Zignego 07861887f1 Start Zewo implementation 2016-05-02 11:19:27 -04:00
Peter Zignego 1c9b7bb011 Remove example 2016-05-02 11:19:13 -04:00
Peter Zignego 59e1e8856d Swift 3 renaming 2016-05-02 10:36:11 -04:00
Peter Zignego 76b33dba0e Setup 2016-05-02 10:35:56 -04:00
Peter Zignego 6040d8deaa Clean up 2016-05-02 10:35:38 -04:00
50 changed files with 1753 additions and 3671 deletions
+5 -6
View File
@@ -16,11 +16,10 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
*.DS_Store
# SwiftPM
Packages/
.build
Packages/
*.xcodeproj/
*.DS_Store
# CocoaPods
#
@@ -29,10 +28,10 @@ Packages/
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
Pods/
SlackKit.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Carthage/Checkouts
# Carthage/Checkouts
Carthage/Build
+1 -1
View File
@@ -1 +1 @@
3.0
3.0.2
-2
View File
@@ -1,2 +0,0 @@
git "https://github.com/daltoniam/Starscream"
git "https://github.com/pvzig/swifter.git"
-2
View File
@@ -1,2 +0,0 @@
git "https://github.com/daltoniam/Starscream" "2.0.0"
git "https://github.com/pvzig/swifter.git" "3.0.2"
+9
View File
@@ -0,0 +1,9 @@
import PackageDescription
let package = Package(
name: "echobot",
targets: [],
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0, minor: 0),
]
)
+35
View File
@@ -0,0 +1,35 @@
import Foundation
import SlackKit
class Echobot: MessageEventsDelegate {
let client: SlackClient
init(token: String) {
client = SlackClient(apiToken: token)
client.messageEventsDelegate = self
}
// MARK: MessageEventsDelegate
func sent(_ message: Message, client: SlackClient) {}
func changed(_ message: Message, client: SlackClient) {}
func deleted(_ message: Message?, client: SlackClient) {}
func received(_ message: Message, client: SlackClient) {
listen(message: message)
}
// MARK: Echobot Internal Logic
private func listen(message: Message) {
if let channel = message.channel, let text = message.text, let id = client.authenticatedUser?.id {
if id != message.user && message.user != nil {
client.webAPI.sendMessage(channel:channel, text: text, linkNames: true, success: {(response) in
}, failure: { (error) in
print("Echobot failed to reply due to error:\(error)")
})
}
}
}
}
let echobot = Echobot(token: "xoxb-SLACK_API_TOKEN")
echobot.client.connect()
+8
View File
@@ -0,0 +1,8 @@
import PackageDescription
let package = Package(
name: "leaderboard",
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0, minor: 0),
]
)
+141
View File
@@ -0,0 +1,141 @@
import Foundation
import SlackKit
class Leaderboard: MessageEventsDelegate {
var leaderboard: [String: Int] = [String: Int]()
let atSet = CharacterSet(charactersIn: "@")
let client: SlackClient
init(token: String) {
client = SlackClient(apiToken: token)
client.messageEventsDelegate = self
}
enum Command: String {
case leaderboard = "leaderboard"
}
enum Trigger: String {
case plusPlus = "++"
case minusMinus = "--"
}
// MARK: MessageEventsDelegate
func sent(_ message: Message, client: SlackClient) {}
func changed(_ message: Message, client: SlackClient) {}
func deleted(_ message: Message?, client: SlackClient) {}
func received(_ message: Message, client: SlackClient) {
listen(message: message)
}
// MARK: Leaderboard Internal Logic
private func listen(message: Message) {
if let id = client.authenticatedUser?.id, let text = message.text {
if text.lowercased().contains(Command.leaderboard.rawValue) && text.contains(id) {
handleCommand(command: .leaderboard, channel: message.channel)
}
}
if message.text?.contains(Trigger.plusPlus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .plusPlus)
}
if message.text?.contains(Trigger.minusMinus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .minusMinus)
}
}
private func handleMessageWithTrigger(message: Message, trigger: Trigger) {
if let text = message.text, let start = text.range(of: "@")?.lowerBound, let end = text.range(of: trigger.rawValue)?.lowerBound {
let string = String(text.characters[start...end].dropLast().dropFirst())
let users = client.users.values.filter{$0.id == self.userID(string: string)}
if users.count > 0 {
let idString = userID(string: string)
initalizationForValue(dictionary: &leaderboard, value: idString)
scoringForValue(dictionary: &leaderboard, value: idString, trigger: trigger)
} else {
initalizationForValue(dictionary: &leaderboard, value: string)
scoringForValue(dictionary: &leaderboard, value: string, trigger: trigger)
}
}
}
private func handleCommand(command: Command, channel:String?) {
switch command {
case .leaderboard:
if let id = channel {
client.webAPI.sendMessage(channel:id, text: "Leaderboard", linkNames: true, attachments: [constructLeaderboardAttachment()], success: {(response) in
print(response)
}, failure: { (error) in
print("Leaderboard failed to post due to error:\(error)")
})
}
}
}
private func initalizationForValue( dictionary: inout [String: Int], value: String) {
if dictionary[value] == nil {
dictionary[value] = 0
}
}
private func scoringForValue( dictionary: inout [String: Int], value: String, trigger: Trigger) {
switch trigger {
case .plusPlus:
dictionary[value]?+=1
case .minusMinus:
dictionary[value]?-=1
}
}
// MARK: Leaderboard Interface
private func constructLeaderboardAttachment() -> Attachment? {
let 💯 = AttachmentField(title: "💯", value: swapIDsForNames(string: topItems(dictionary: &leaderboard)), short: true)
let 💩 = AttachmentField(title: "💩", value: swapIDsForNames(string: bottomItems(dictionary: &leaderboard)), short: true)
return Attachment(fallback: "Leaderboard", title: "Leaderboard", colorHex: AttachmentColor.good.rawValue, text: "", fields: [💯, 💩])
}
private func topItems(dictionary: inout [String: Int]) -> String {
let sortedKeys = dictionary.keys.sorted(by: { (k1: String, k2: String) -> Bool in
return dictionary[k1]! > dictionary[k2]!
}).filter({ dictionary[$0]! > 0})
let sortedValues = dictionary.values.sorted(by: {$0 > $1}).filter({$0 > 0})
return leaderboardString(keys: sortedKeys, values: sortedValues)
}
private func bottomItems( dictionary: inout [String: Int]) -> String {
let sortedKeys = dictionary.keys.sorted(by: { (k1: String, k2: String) -> Bool in
return dictionary[k1]! < dictionary[k2]!
}).filter({ dictionary[$0]! < 0})
let sortedValues = dictionary.values.sorted(by: {$0 < $1}).filter({$0 < 0})
return leaderboardString(keys: sortedKeys, values: sortedValues)
}
private func leaderboardString(keys: [String], values: [Int]) -> String {
var returnValue = ""
for i in 0..<values.count {
returnValue += keys[i] + " (" + "\(values[i])" + ")\n"
}
return returnValue
}
// MARK: - Utilities
private func swapIDsForNames(string: String) -> String {
var returnString = string
for key in client.users.keys {
if let name = client.users[key]?.name {
if returnString.contains(key) {
returnString = returnString.replacingOccurrences(of: key, with: "@"+name)
}
}
}
return returnString
}
private func userID(string: String) -> String {
return string.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
}
}
let leaderboard = Leaderboard(token: "xoxb-SLACK_API_TOKEN")
leaderboard.client.connect()
+9
View File
@@ -0,0 +1,9 @@
import PackageDescription
let package = Package(
name: "robot-or-not-bot",
targets: [],
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0, minor: 0),
]
)
@@ -0,0 +1,117 @@
import Foundation
import SlackKit
class RobotOrNotBot: MessageEventsDelegate {
let verdicts: [String:Bool] = [
"Mr. Roboto" : false,
"Service Kiosks": false,
"Darth Vader": false,
"K-9": true,
"Emotions": false,
"Self-Driving Cars": false,
"Telepresence Robots": false,
"Roomba": true,
"Assembly-Line Robot": false,
"ASIMO": false,
"KITT": false,
"USS Enterprise": false,
"Transformers": true,
"Jaegers": false,
"The Major": false,
"Siri": false,
"The Terminator": true,
"Commander Data": false,
"Marvin the Paranoid Android": true,
"Pinocchio": false,
"Droids": true,
"Hitchbot": false,
"Mars Rovers": false,
"Space Probes": false,
"Sasquatch": false,
"Toaster": false,
"Toaster Oven": false,
"Cylons": false,
"V'ger": true,
"Ilia Robot": false,
"The TARDIS": false,
"Johnny 5": true,
"Twiki": true,
"Dr. Theopolis": false,
"robots.txt": false,
"Lobot": false,
"Vicki": true,
"GlaDOS": false,
"Turrets": true,
"Wheatley": true,
"Herbie the Love Bug": false,
"Iron Man": false,
"Ultron": false,
"The Vision": false,
"Clockwork Droids": false,
"Podcasts": false,
"Cars": false,
"Swimming Pool Cleaners": false,
"Burritos": false,
"Prince Robot IV": false,
"Daleks": false,
"Cybermen": false,
"The Internet of Things": false,
"Nanobots": true,
"Two Intermeshed Gears": false,
"Crow T. Robot": true,
"Tom Servo": true,
"Thomas and Friends": false,
"Replicants": false,
"Chatbots": false,
"Agents": false,
"Lego Simulated Worm Toy": true,
"Ghosts": false,
"Exos": true,
"Rasputin": false,
"Tamagotchi": false,
"T-1000": true,
"The Tin Woodman": false,
"Mic N. The Robot": true,
"Robot Or Not Bot": false
]
let client: SlackClient
init(token: String) {
client = SlackClient(apiToken: token)
client.messageEventsDelegate = self
}
// MARK: MessageEventsDelegate
func received(_ message: Message, client: SlackClient) {
if let id = client.authenticatedUser?.id {
if message.text?.contains(id) == true {
handleMessage(message: message)
}
}
}
func changed(_ message: Message, client: SlackClient) {}
func deleted(_ message: Message?, client: SlackClient) {}
func sent(_ message: Message, client: SlackClient) {}
private func handleMessage(message: Message) {
if let text = message.text?.lowercased(), let channel = message.channel {
for (robot, verdict) in verdicts {
let lowerbot = robot.lowercased()
if text.contains(lowerbot) {
if verdict == true {
client.webAPI.addReaction(name: "robot_face", channel: channel, timestamp: message.ts, success: nil, failure: nil)
} else {
client.webAPI.addReaction(name: "no_entry_sign", channel: channel, timestamp: message.ts, success: nil, failure: nil)
}
return
}
}
client.webAPI.addReaction(name: "question", channel: channel, timestamp: message.ts, success: nil, failure: nil)
}
}
}
let slackbot = RobotOrNotBot(token: "xoxb-SLACK_API_TOKEN")
slackbot.client.connect()
+11 -7
View File
@@ -1,5 +1,5 @@
//
// SlackKit.h
// Package.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -21,10 +21,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
import PackageDescription
//! Project version number for SlackKit.
FOUNDATION_EXPORT double SlackKitVersionNumber;
//! Project version string for SlackKit.
FOUNDATION_EXPORT const unsigned char SlackKitVersionString[];
let package = Package(
name: "SlackKit",
targets: [],
dependencies: [
.Package(url: "https://github.com/Zewo/WebSocketClient", majorVersion: 0, minor: 14),
.Package(url: "https://github.com/Zewo/HTTPClient.git", majorVersion: 0, minor: 14)
],
exclude: ["Examples"]
)
+136 -200
View File
@@ -1,125 +1,72 @@
![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) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage)
## SlackKit: A Swift Slack Client Library
### Description
![Swift Version](https://img.shields.io/badge/Swift-DEVELOPMENT--SNAPSHOT--2016--05--09--a-orange.svg) ![Plaforms](https://img.shields.io/badge/Platforms-macOS,linux-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)
##Alpha Linux Slack Client Library
###Description
This is a Slack client library for Linux written 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 by [bot users](https://api.slack.com/bot-users).
This is a Slack client library for OS X, iOS, and tvOS written 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 Slacks [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).
###Disclaimer: The linux version of SlackKit is a pre-release alpha. Feel free to report issues you come across.
This is the **Swift 3** branch of SlackKit. SlackKit also has support for [Swift 2.3](https://github.com/pvzig/SlackKit/tree/swift2.3) and [Linux](https://github.com/pvzig/SlackKit/tree/linux).
###Installation
#### Building the SlackKit Framework
To build the SlackKit project directly, first build the dependencies using Carthage or CocoaPods. To use the framework in your application, install it in one of the following ways:
####Swift Package Manager
Add SlackKit to your Package.swift
### Installation
```swift
import PackageDescription
#### Carthage
Add SlackKit to your Cartfile:
```
github "https://github.com/pvzig/slackkit.git"
```
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"
let package = Package(
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0, minor: 0)
]
)
```
Drag the built `SlackKit.framework` into your Xcode project.
####Development
1. Install Homebrew: `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
2. Install `swiftenv`: `brew install kylef/formulae/swiftenv`
3. Configure your shell: `echo 'if which swiftenv > /dev/null; then eval "$(swiftenv init -)"; fi' >> ~/.bash_profile`
4. Download and install the latest Zewo compatible snapshot:
```
swiftenv install DEVELOPMENT-SNAPSHOT-2016-05-09-a
swiftenv local DEVELOPMENT-SNAPSHOT-2016-05-09-a
```
5. Install and Link OpenSSL: `brew install openssl`, `brew link openssl --force`
To build an application that uses SlackKit in Xcode, simply use SwiftPM. (For the 05-03 snapshot you must run `swift build` before generating an Xcode project:
```
swift build
swift build -Xlinker -L$(pwd)/.build/debug/ -Xswiftc -I/usr/local/include -Xlinker -L/usr/local/lib -X
```
#### ~~CocoaPods~~
#### ~~Swift Package Manager~~
SlackKit doesnt currently build correctly using CocoaPods or Swift Package Manager and Swift 3. Im hoping to restore support for both soon.
To use the library in your project import it:
```
import SlackKit
```
### Usage
####Deployment
Deploy your application to Heroku using [this buildpack](https://github.com/pvzig/heroku-buildpack-swift). For more detailed instructions please see [this post](https://medium.com/@pvzig/building-slack-bots-in-swift-b99e243e444c).
#### OAuth
Slack has [many different oauth scopes](https://api.slack.com/docs/oauth-scopes) that can be combined in different ways. If your application does not request the proper OAuth scopes, your API calls will fail.
###Usage
To use SlackKit you'll need a bearer token which identifies a single user. You can generate a [full access token or create one using OAuth 2](https://api.slack.com/web).
If you authenticate using OAuth and the Add to Slack or Sign in with Slack buttons this is handled for you.
If you wish to make OAuth requests yourself, you can generate them using the `authorizeRequest` function on `SlackKit`s `oauth` property:
Once you have a token, initialize a client instance using it:
```swift
func authorizeRequest(scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil)
let client = Client(apiToken: "YOUR_SLACK_API_TOKEN")
```
For local development of things like OAuth, slash commands, and message buttons that require connecting over `https`, you may want to use a tool like [ngrok](https://ngrok.com) or [localtunnel](http://localtunnel.me).
#### Incoming Webhooks
After [configuring your incoming webhook in Slack](https://my.slack.com/services/new/incoming-webhook/), initialize IncomingWebhook with the provided URL and use `postMessage` to send messages.
If you want to receive messages from the Slack RTM API, connect to it.
```swift
let incoming = IncomingWebhook(url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX")
let message = Response(text: "Hello, World!")
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 webhook = WebhookServer(token: "SLASH-COMMAND-TOKEN", route: "hello_world", response: response)
webhook.start()
```
When a user enters that slash command, it will hit your configured route and return the response you specified.
To add additional routes and responses, you can use WebhookServers addRoute function:
```swift
func addRoute(route: String, response: Response)
```
#### Message Buttons
If you are developing a Slack App and are authorizing using OAuth, you can use [message buttons](https://api.slack.com/docs/message-buttons).
To send messages with actions, add them to an attachment:
```swift
let helloAction = Action(name: "hello_world", text: "Hello, World!")
let attachment = Attachment(fallback: "Hello World Attachment", title: "Attachment with an Action Button", callbackID: "helloworld", actions: [helloAction])
```
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 responder = MessageActionResponder(responses: [(action, response)])
let server = MessageActionServer(token: "SLACK-APP-VERIFICATION-TOKEN", route: "actions", responder: responder)
server.start()
```
#### Bot Users
To deploy a bot user using SlackKit you'll need a bearer token which identifies a single user. You can generate a [full access token or create one using OAuth 2](https://api.slack.com/web).
Initialize a SlackKit instance using your [applications Client ID and Client Secret](https://api.slack.com/apps) to set up SlackKit for OAuth authorization:
```swift
let bot = SlackKit(clientID: "CLIENT_ID", clientSecret: "CLIENT_SECRET")
```
or use a manually acquired token:
```swift
let bot = SlackKit(withAPIToken: "xoxp-YOUR-SLACK-API-TOKEN")
```
#### Client Connection Options
You can also set options for a ping/pong interval, timeout interval, and automatic reconnection:
```swift
let options = ClientOptions(pingInterval: 2, timeout: 10, reconnect: false)
let bot = SlackKit(clientID: "CLIENT_ID", clientSecret: "CLIENT_SECRET", clientOptions: options)
client.connect()
```
Once connected, the client will begin to consume any messages sent by the Slack RTM API.
#### Web API Methods
####Web API Methods
SlackKit currently supports the a subset of the Slack Web APIs that are available to bot users:
- api.test
- auth.revoke
- auth.test
- channels.history
- channels.info
@@ -135,7 +82,6 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- files.comments.edit
- files.comments.delete
- files.delete
- files.info
- files.upload
- groups.close
- groups.history
@@ -155,7 +101,6 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- mpim.list
- mpim.mark
- mpim.open
- oauth.access
- pins.add
- pins.list
- pins.remove
@@ -175,121 +120,112 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
They can be accessed through a Client objects `webAPI` property:
```swift
client.webAPI.authenticationTest({(auth) in
print(auth)
}, failure: {(error) in
print(error)
})
```
#### Delegate methods
To receive delegate callbacks for events, register an object as the delegate for those events using the `onClientInitalization` block:
```swift
let bot = SlackKit(clientID: clientID, clientSecret: clientSecret)
bot.onClientInitalization = { (client: Client) in
DispatchQueue.main.async(execute: {
client.messageEventsDelegate = self
})
client.webAPI.authenticationTest({
(authenticated) -> Void in
print(authenticated)
}){(error) -> Void in
print(error)
}
```
Delegate callbacks contain a reference to the Client where the event occurred.
####Delegate methods
To receive delegate callbacks for certain events, register an object as the delegate for those events:
```swift
client.slackEventsDelegate = self
```
There are a number of delegates that you can set to receive callbacks for certain events.
##### ConnectionEventsDelegate
#####SlackEventsDelegate
```swift
connected(_ client: Client)
disconnected(_ client: Client)
connectionFailed(_ client: Client, error: SlackError)
```
##### MessageEventsDelegate
```swift
sent(_ message: Message, client: Client)
received(_ message: Message, client: Client)
changed(_ message: Message, client: Client)
deleted(_ message: Message?, client: Client)
```
##### ChannelEventsDelegate
```swift
userTypingIn(_ channel: Channel, user: User, client: Client)
marked(_ channel: Channel, timestamp: String, client: Client)
created(_ channel: Channel, client: Client)
deleted(_ channel: Channel, client: Client)
renamed(_ channel: Channel, client: Client)
archived(_ channel: Channel, client: Client)
historyChanged(_ channel: Channel, client: Client)
joined(_ channel: Channel, client: Client)
left(_ channel: Channel, client: Client)
```
##### DoNotDisturbEventsDelegate
```swift
updated(_ status: DoNotDisturbStatus, client: Client)
userUpdated(_ status: DoNotDisturbStatus, user: User, client: Client)
```
##### GroupEventsDelegate
```swift
opened(_ group: Channel, client: Client)
```
##### FileEventsDelegate
```swift
processed(_ file: File, client: Client)
madePrivate(_ file: File, client: Client)
deleted(_ file: File, client: Client)
commentAdded(_ file: File, comment: Comment, client: Client)
commentEdited(_ file: File, comment: Comment, client: Client)
commentDeleted(_ file: File, comment: Comment, client: Client)
```
##### PinEventsDelegate
```swift
pinned(_ item: Item, channel: Channel?, client: Client)
unpinned(_ item: Item, channel: Channel?, client: Client)
```
##### StarEventsDelegate
```swift
starred(_ item: Item, starred: Bool, _ client: Client)
```
##### ReactionEventsDelegate
```swift
added(_ reaction: String, item: Item, itemUser: String, client: Client)
removed(_ reaction: String, item: Item, itemUser: String, client: Client)
```
##### SlackEventsDelegate
```swift
preferenceChanged(_ preference: String, value: Any?, client: Client)
userChanged(_ user: User, client: Client)
presenceChanged(_ user: User, presence: String, client: Client)
manualPresenceChanged(_ user: User, presence: String, client: Client)
botEvent(_ bot: Bot, client: Client)
```
##### TeamEventsDelegate
```swift
userJoined(_ user: User, client: Client)
planChanged(_ plan: String, client: Client)
preferencesChanged(_ preference: String, value: Any?, client: Client)
nameChanged(_ name: String, client: Client)
domainChanged(_ domain: String, client: Client)
emailDomainChanged(_ domain: String, client: Client)
emojiChanged(_ client: Client)
```
##### SubteamEventsDelegate
```swift
event(_ userGroup: UserGroup, client: Client)
selfAdded(_ subteamID: String, client: Client)
selfRemoved(_ subteamID: String, client: Client)
```
##### TeamProfileEventsDelegate
```swift
changed(_ profile: CustomProfile, client: Client)
deleted(_ profile: CustomProfile, client: Client)
reordered(_ profile: CustomProfile, client: Client)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
func userChanged(user: User)
func presenceChanged(user: User?, presence: String?)
func manualPresenceChanged(user: User?, presence: String?)
func botEvent(bot: Bot)
```
### Examples
[Check out example applications here.](https://github.com/pvzig/SlackKit-examples)
#####MessageEventsDelegate
```swift
func messageSent(message: Message)
func messageReceived(message: Message)
func messageChanged(message: Message)
func messageDeleted(message: Message?)
```
### Get In Touch
#####ChannelEventsDelegate
```swift
func userTyping(channel: Channel?, user: User?)
func channelMarked(channel: Channel, timestamp: String?)
func channelCreated(channel: Channel)
func channelDeleted(channel: Channel)
func channelRenamed(channel: Channel)
func channelArchived(channel: Channel)
func channelHistoryChanged(channel: Channel)
func channelJoined(channel: Channel)
func channelLeft(channel: Channel)
```
#####DoNotDisturbEventsDelegate
```swift
doNotDisturbUpdated(dndStatus: DoNotDisturbStatus)
doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User?)
```
#####GroupEventsDelegate
```swift
func groupOpened(group: Channel)
```
#####FileEventsDelegate
```swift
func fileProcessed(file: File)
func fileMadePrivate(file: File)
func fileDeleted(file: File)
func fileCommentAdded(file: File, comment: Comment)
func fileCommentEdited(file: File, comment: Comment)
func fileCommentDeleted(file: File, comment: Comment)
```
#####PinEventsDelegate
```swift
func itemPinned(item: Item?, channel: Channel?)
func itemUnpinned(item: Item?, channel: Channel?)
```
#####StarEventsDelegate
```swift
func itemStarred(item: Item, star: Bool)
```
#####ReactionEventsDelegate
```swift
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
```
#####TeamEventsDelegate
```swift
func teamJoined(user: User)
func teamPlanChanged(plan: String)
func teamPreferencesChanged(preference: String, value: AnyObject)
func teamNameChanged(name: String)
func teamDomainChanged(domain: String)
func teamEmailDomainChanged(domain: String)
func teamEmojiChanged()
```
#####SubteamEventsDelegate
```swift
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
```
###Get In Touch
[@pvzig](https://twitter.com/pvzig)
<peter@launchsoft.co>
-970
View File
@@ -1,970 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2601D61B1C7646B80012BF22 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
260EC2351C4DC61D0093B253 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993901CE90C87004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993981CE90EE0004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993991CE90EE0004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
2639939F1CE90EE0004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993A01CE90EE0004A6E93 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993A21CE90EE0004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993A31CE90EE0004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993A61CE90EE0004A6E93 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993B71CE90EED004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993B81CE90EED004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
263993BA1CE90EED004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
263993BB1CE90EED004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
263993BE1CE90EED004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993BF1CE90EED004A6E93 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993C11CE90EED004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993C21CE90EED004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993C51CE90EED004A6E93 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
2678B5941D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5951D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5961D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5971D3151C600CE521A /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
2678B5981D3151C900CE521A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
2678B5991D3151CD00CE521A /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
269B475A1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475B1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475C1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475E1D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B475F1D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B47601D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B47621D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47631D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47641D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47661D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47671D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47681D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47C71D3AE25B0042D137 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
269B47C81D3AE25B0042D137 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
269B47C91D3AE2620042D137 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
269B47CA1D3AE2670042D137 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
269B47CE1D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
269B47CF1D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
269B47D01D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
26B30B6C1D289FA0004D4AB5 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B30B6B1D289FA0004D4AB5 /* Swifter.framework */; };
26B30B881D297A98004D4AB5 /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
26B30B901D298E08004D4AB5 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
26B30B921D298E12004D4AB5 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
26B30B941D298E78004D4AB5 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
26B30B951D298E78004D4AB5 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
26B30B961D298EE1004D4AB5 /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
26B30B971D298EED004D4AB5 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
26B30B991D298F25004D4AB5 /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
26B30B9A1D298F25004D4AB5 /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
26B30BB61D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BB71D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BB81D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BC31D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC41D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC51D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC71D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26B30BC81D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26B30BC91D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
26EC14C11D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C21D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C31D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C91D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CA1D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CB1D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CC1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CD1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CE1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CF1D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D01D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D11D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D21D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D31D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D41D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D51D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14D61D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14D71D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14DA1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DB1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DC1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DD1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14DE1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14DF1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14E11D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E21D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E31D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E61D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E71D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E81D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E91D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14EA1D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14EB1D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14F91D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC14FA1D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC14FB1D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC15011D260B1000FD3A53 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
26EC15021D260B1000FD3A53 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
26F76EB21D40318F00C3A3DD /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B30B6E1D289FB2004D4AB5 /* Swifter.framework */; };
26F76EB31D40319700C3A3DD /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 269B47CB1D3AE5670042D137 /* Swifter.framework */; };
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2601D61A1C7646B80012BF22 /* SlackError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackError.swift; path = Sources/SlackError.swift; sourceTree = "<group>"; };
26072A341BB48B3A00CD650C /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
260EC2301C4DC61D0093B253 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = "<group>"; };
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkInterface.swift; path = Sources/NetworkInterface.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* WebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebAPI.swift; path = Sources/WebAPI.swift; sourceTree = "<group>"; };
263993B21CE90EE0004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
263993D11CE90EED004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2661A6A41BBF62FF0026F67B /* SlackKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SlackKit.h; sourceTree = "<group>"; };
2678B5931D3151B900CE521A /* AuthorizeResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthorizeResponse.swift; path = Sources/AuthorizeResponse.swift; sourceTree = "<group>"; };
268E46131CE8F79D009F19CC /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-iOS.plist"; path = "Supporting Files/Info-iOS.plist"; sourceTree = "<group>"; };
268E46141CE8F79D009F19CC /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-tvOS.plist"; path = "Supporting Files/Info-tvOS.plist"; sourceTree = "<group>"; };
268E46151CE8F79D009F19CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Supporting Files/Info.plist"; sourceTree = "<group>"; };
269B47591D3493DE0042D137 /* OAuthResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OAuthResponse.swift; path = Sources/OAuthResponse.swift; sourceTree = "<group>"; };
269B475D1D3538E90042D137 /* SlackKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackKit.swift; path = Sources/SlackKit.swift; sourceTree = "<group>"; };
269B47611D3544240042D137 /* IncomingWebhook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IncomingWebhook.swift; path = Sources/IncomingWebhook.swift; sourceTree = "<group>"; };
269B47651D39AAA80042D137 /* MessageActionResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionResponder.swift; path = Sources/MessageActionResponder.swift; sourceTree = "<group>"; };
269B47CB1D3AE5670042D137 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/tvOS/Swifter.framework; sourceTree = "<group>"; };
269B47CD1D3C22FC0042D137 /* ClientOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClientOptions.swift; path = Sources/ClientOptions.swift; sourceTree = "<group>"; };
26B30B6B1D289FA0004D4AB5 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/Mac/Swifter.framework; sourceTree = "<group>"; };
26B30B6E1D289FB2004D4AB5 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/iOS/Swifter.framework; sourceTree = "<group>"; };
26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionRequest.swift; path = Sources/MessageActionRequest.swift; sourceTree = "<group>"; };
26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionServer.swift; path = Sources/MessageActionServer.swift; sourceTree = "<group>"; };
26B30B911D298E12004D4AB5 /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Server.swift; path = Sources/Server.swift; sourceTree = "<group>"; };
26B30B931D298E78004D4AB5 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Sources/Response.swift; sourceTree = "<group>"; };
26B30B981D298F25004D4AB5 /* WebhookRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebhookRequest.swift; path = Sources/WebhookRequest.swift; sourceTree = "<group>"; };
26B30BB51D2BC2E4004D4AB5 /* Scope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Scope.swift; path = Sources/Scope.swift; sourceTree = "<group>"; };
26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OAuthServer.swift; path = Sources/OAuthServer.swift; sourceTree = "<group>"; };
26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthorizeRequest.swift; path = Sources/AuthorizeRequest.swift; sourceTree = "<group>"; };
26BBA1871C398E3C00BF7225 /* Bot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bot.swift; path = Sources/Bot.swift; sourceTree = "<group>"; };
26BBA1881C398E3C00BF7225 /* Channel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Channel.swift; path = Sources/Channel.swift; sourceTree = "<group>"; };
26BBA1891C398E3C00BF7225 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Client.swift; path = Sources/Client.swift; sourceTree = "<group>"; };
26BBA18A1C398E3C00BF7225 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Event.swift; path = Sources/Event.swift; sourceTree = "<group>"; };
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventDelegate.swift; path = Sources/EventDelegate.swift; sourceTree = "<group>"; };
26BBA18E1C398E3C00BF7225 /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = File.swift; path = Sources/File.swift; sourceTree = "<group>"; };
26BBA18F1C398E3C00BF7225 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Message.swift; path = Sources/Message.swift; sourceTree = "<group>"; };
26BBA1901C398E3C00BF7225 /* Team.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Team.swift; path = Sources/Team.swift; sourceTree = "<group>"; };
26BBA1921C398E3C00BF7225 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = Sources/User.swift; sourceTree = "<group>"; };
26BBA1931C398E3C00BF7225 /* UserGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserGroup.swift; path = Sources/UserGroup.swift; sourceTree = "<group>"; };
26DF40341C7A0FA300E19241 /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Attachment.swift; path = Sources/Attachment.swift; sourceTree = "<group>"; };
26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentField.swift; path = Sources/AttachmentField.swift; sourceTree = "<group>"; };
26EC14C41D1EF17400FD3A53 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Comment.swift; path = Sources/Comment.swift; sourceTree = "<group>"; };
26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomProfile.swift; path = Sources/CustomProfile.swift; sourceTree = "<group>"; };
26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomProfileField.swift; path = Sources/CustomProfileField.swift; sourceTree = "<group>"; };
26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DoNotDisturbStatus.swift; path = Sources/DoNotDisturbStatus.swift; sourceTree = "<group>"; };
26EC14C81D1EF17400FD3A53 /* Edited.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Edited.swift; path = Sources/Edited.swift; sourceTree = "<group>"; };
26EC14D81D1EF17E00FD3A53 /* History.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = History.swift; path = Sources/History.swift; sourceTree = "<group>"; };
26EC14D91D1EF17E00FD3A53 /* Item.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Item.swift; path = Sources/Item.swift; sourceTree = "<group>"; };
26EC14E01D1EF18700FD3A53 /* Reaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Reaction.swift; path = Sources/Reaction.swift; sourceTree = "<group>"; };
26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TeamIcon.swift; path = Sources/TeamIcon.swift; sourceTree = "<group>"; };
26EC14E51D1EF18F00FD3A53 /* Topic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Topic.swift; path = Sources/Topic.swift; sourceTree = "<group>"; };
26EC14F81D1F355A00FD3A53 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Sources/Action.swift; sourceTree = "<group>"; };
26EC15001D260B1000FD3A53 /* WebhookServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebhookServer.swift; path = Sources/WebhookServer.swift; sourceTree = "<group>"; };
4307A07F1CC6D0910011D5DE /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/Mac/Starscream.framework; sourceTree = "<group>"; };
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+Utilities.swift"; path = "Sources/Client+Utilities.swift"; sourceTree = "<group>"; };
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventDispatching.swift"; path = "Sources/Client+EventDispatching.swift"; sourceTree = "<group>"; };
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventHandling.swift"; path = "Sources/Client+EventHandling.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
26072A301BB48B3A00CD650C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */,
26B30B6C1D289FA0004D4AB5 /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AA1CE90EE0004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */,
26F76EB21D40318F00C3A3DD /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993C91CE90EED004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */,
26F76EB31D40319700C3A3DD /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
26072A2A1BB48B3A00CD650C = {
isa = PBXGroup;
children = (
2661A6811BBF60E60026F67B /* SlackKit */,
26072A351BB48B3A00CD650C /* Products */,
CA70A3A1A9A1A259960DFBCF /* Frameworks */,
);
sourceTree = "<group>";
};
26072A351BB48B3A00CD650C /* Products */ = {
isa = PBXGroup;
children = (
26072A341BB48B3A00CD650C /* SlackKit.framework */,
263993B21CE90EE0004A6E93 /* SlackKit.framework */,
263993D11CE90EED004A6E93 /* SlackKit.framework */,
);
name = Products;
sourceTree = "<group>";
};
2661A6811BBF60E60026F67B /* SlackKit */ = {
isa = PBXGroup;
children = (
26BBA1891C398E3C00BF7225 /* Client.swift */,
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */,
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */,
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */,
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */,
260EC2301C4DC61D0093B253 /* Extensions.swift */,
269B47611D3544240042D137 /* IncomingWebhook.swift */,
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */,
26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */,
269B47651D39AAA80042D137 /* MessageActionResponder.swift */,
26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */,
26B30B911D298E12004D4AB5 /* Server.swift */,
269B475D1D3538E90042D137 /* SlackKit.swift */,
260EC2321C4DC61D0093B253 /* WebAPI.swift */,
26EC15001D260B1000FD3A53 /* WebhookServer.swift */,
26EC14B31D1B974500FD3A53 /* Models */,
268E46161CE8F7A2009F19CC /* Supporting Files */,
);
path = SlackKit;
sourceTree = "<group>";
};
268E46161CE8F7A2009F19CC /* Supporting Files */ = {
isa = PBXGroup;
children = (
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
268E46151CE8F79D009F19CC /* Info.plist */,
268E46131CE8F79D009F19CC /* Info-iOS.plist */,
268E46141CE8F79D009F19CC /* Info-tvOS.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
26B30B9B1D29AB1E004D4AB5 /* Server */ = {
isa = PBXGroup;
children = (
26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */,
26B30B931D298E78004D4AB5 /* Response.swift */,
26B30B981D298F25004D4AB5 /* WebhookRequest.swift */,
);
name = Server;
sourceTree = "<group>";
};
26B30BB91D2BC2EA004D4AB5 /* Auth */ = {
isa = PBXGroup;
children = (
26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */,
2678B5931D3151B900CE521A /* AuthorizeResponse.swift */,
26B30BB51D2BC2E4004D4AB5 /* Scope.swift */,
269B47591D3493DE0042D137 /* OAuthResponse.swift */,
);
name = Auth;
sourceTree = "<group>";
};
26EC14B31D1B974500FD3A53 /* Models */ = {
isa = PBXGroup;
children = (
26EC14F81D1F355A00FD3A53 /* Action.swift */,
26DF40341C7A0FA300E19241 /* Attachment.swift */,
26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */,
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
269B47CD1D3C22FC0042D137 /* ClientOptions.swift */,
26EC14C41D1EF17400FD3A53 /* Comment.swift */,
26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */,
26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */,
26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */,
26EC14C81D1EF17400FD3A53 /* Edited.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26EC14D81D1EF17E00FD3A53 /* History.swift */,
26EC14D91D1EF17E00FD3A53 /* Item.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
26EC14E01D1EF18700FD3A53 /* Reaction.swift */,
2601D61A1C7646B80012BF22 /* SlackError.swift */,
26BBA1901C398E3C00BF7225 /* Team.swift */,
26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */,
26EC14E51D1EF18F00FD3A53 /* Topic.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
26B30BB91D2BC2EA004D4AB5 /* Auth */,
26B30B9B1D29AB1E004D4AB5 /* Server */,
);
name = Models;
sourceTree = "<group>";
};
CA70A3A1A9A1A259960DFBCF /* Frameworks */ = {
isa = PBXGroup;
children = (
269B47CB1D3AE5670042D137 /* Swifter.framework */,
26B30B6E1D289FB2004D4AB5 /* Swifter.framework */,
26B30B6B1D289FA0004D4AB5 /* Swifter.framework */,
4307A07F1CC6D0910011D5DE /* Starscream.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
26072A311BB48B3A00CD650C /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993901CE90C87004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AC1CE90EE0004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CB1CE90EED004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
26072A331BB48B3A00CD650C /* SlackKit OS X */ = {
isa = PBXNativeTarget;
buildConfigurationList = 26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit OS X" */;
buildPhases = (
26072A2F1BB48B3A00CD650C /* Sources */,
26072A301BB48B3A00CD650C /* Frameworks */,
26072A311BB48B3A00CD650C /* Headers */,
26072A321BB48B3A00CD650C /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SlackKit OS X";
productName = SlackRTMKit;
productReference = 26072A341BB48B3A00CD650C /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
263993951CE90EE0004A6E93 /* SlackKit iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit iOS" */;
buildPhases = (
263993961CE90EE0004A6E93 /* Sources */,
263993AA1CE90EE0004A6E93 /* Frameworks */,
263993AC1CE90EE0004A6E93 /* Headers */,
263993AE1CE90EE0004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SlackKit iOS";
productName = SlackRTMKit;
productReference = 263993B21CE90EE0004A6E93 /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
263993B41CE90EED004A6E93 /* SlackKit tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit tvOS" */;
buildPhases = (
263993B51CE90EED004A6E93 /* Sources */,
263993C91CE90EED004A6E93 /* Frameworks */,
263993CB1CE90EED004A6E93 /* Headers */,
263993CD1CE90EED004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SlackKit tvOS";
productName = SlackRTMKit;
productReference = 263993D11CE90EED004A6E93 /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
26072A2B1BB48B3A00CD650C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Launch Software LLC";
TargetAttributes = {
26072A331BB48B3A00CD650C = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0800;
};
};
};
buildConfigurationList = 26072A2E1BB48B3A00CD650C /* Build configuration list for PBXProject "SlackKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 26072A2A1BB48B3A00CD650C;
productRefGroup = 26072A351BB48B3A00CD650C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
26072A331BB48B3A00CD650C /* SlackKit OS X */,
263993951CE90EE0004A6E93 /* SlackKit iOS */,
263993B41CE90EED004A6E93 /* SlackKit tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
26072A321BB48B3A00CD650C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AE1CE90EE0004A6E93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CD1CE90EED004A6E93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
26072A2F1BB48B3A00CD650C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2678B5941D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26B30B921D298E12004D4AB5 /* Server.swift in Sources */,
269B47CE1D3C22FC0042D137 /* ClientOptions.swift in Sources */,
269B47621D3544240042D137 /* IncomingWebhook.swift in Sources */,
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */,
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */,
26B30B901D298E08004D4AB5 /* MessageActionServer.swift in Sources */,
26B30B941D298E78004D4AB5 /* Response.swift in Sources */,
26EC14CF1D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
269B47661D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */,
26EC14DD1D1EF17E00FD3A53 /* Item.swift in Sources */,
26EC15011D260B1000FD3A53 /* WebhookServer.swift in Sources */,
26EC14CC1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */,
26B30B881D297A98004D4AB5 /* MessageActionRequest.swift in Sources */,
269B475E1D3538E90042D137 /* SlackKit.swift in Sources */,
26B30BC71D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */,
26EC14D21D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */,
26EC14E61D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */,
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */,
26EC14F91D1F355A00FD3A53 /* Action.swift in Sources */,
26B30BC31D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
26EC14D51D1EF17400FD3A53 /* Edited.swift in Sources */,
260EC2351C4DC61D0093B253 /* WebAPI.swift in Sources */,
26EC14E11D1EF18700FD3A53 /* Reaction.swift in Sources */,
26B30BB61D2BC2E4004D4AB5 /* Scope.swift in Sources */,
26EC14E91D1EF18F00FD3A53 /* Topic.swift in Sources */,
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */,
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */,
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */,
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */,
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackError.swift in Sources */,
26EC14DA1D1EF17E00FD3A53 /* History.swift in Sources */,
269B475A1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */,
26B30B991D298F25004D4AB5 /* WebhookRequest.swift in Sources */,
26EC14C91D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C11D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */,
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993961CE90EE0004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26B30B961D298EE1004D4AB5 /* MessageActionRequest.swift in Sources */,
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */,
269B47CF1D3C22FC0042D137 /* ClientOptions.swift in Sources */,
269B47C71D3AE25B0042D137 /* MessageActionServer.swift in Sources */,
263993981CE90EE0004A6E93 /* Channel.swift in Sources */,
26EC14D01D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
263993991CE90EE0004A6E93 /* User.swift in Sources */,
26EC14DE1D1EF17E00FD3A53 /* Item.swift in Sources */,
269B475B1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
26EC15021D260B1000FD3A53 /* WebhookServer.swift in Sources */,
26B30BC41D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
26EC14CD1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */,
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */,
26B30BC81D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
2678B5951D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26EC14D31D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */,
26EC14E71D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */,
26B30B971D298EED004D4AB5 /* Server.swift in Sources */,
2639939F1CE90EE0004A6E93 /* File.swift in Sources */,
26B30B951D298E78004D4AB5 /* Response.swift in Sources */,
26EC14FA1D1F355A00FD3A53 /* Action.swift in Sources */,
26EC14D61D1EF17400FD3A53 /* Edited.swift in Sources */,
263993A01CE90EE0004A6E93 /* WebAPI.swift in Sources */,
26B30B9A1D298F25004D4AB5 /* WebhookRequest.swift in Sources */,
26EC14E21D1EF18700FD3A53 /* Reaction.swift in Sources */,
26EC14EA1D1EF18F00FD3A53 /* Topic.swift in Sources */,
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */,
26B30BB71D2BC2E4004D4AB5 /* Scope.swift in Sources */,
263993A21CE90EE0004A6E93 /* Message.swift in Sources */,
263993A31CE90EE0004A6E93 /* Team.swift in Sources */,
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */,
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */,
263993A61CE90EE0004A6E93 /* SlackError.swift in Sources */,
269B475F1D3538E90042D137 /* SlackKit.swift in Sources */,
26EC14DB1D1EF17E00FD3A53 /* History.swift in Sources */,
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */,
269B47631D3544240042D137 /* IncomingWebhook.swift in Sources */,
269B47671D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
26EC14CA1D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C21D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */,
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993B51CE90EED004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */,
263993B71CE90EED004A6E93 /* Channel.swift in Sources */,
269B47D01D3C22FC0042D137 /* ClientOptions.swift in Sources */,
26EC14D11D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
263993B81CE90EED004A6E93 /* User.swift in Sources */,
26EC14DF1D1EF17E00FD3A53 /* Item.swift in Sources */,
2678B5981D3151C900CE521A /* Response.swift in Sources */,
269B475C1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
26EC14CE1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
269B47C91D3AE2620042D137 /* Server.swift in Sources */,
263993BA1CE90EED004A6E93 /* Client.swift in Sources */,
263993BB1CE90EED004A6E93 /* Event.swift in Sources */,
269B47CA1D3AE2670042D137 /* WebhookServer.swift in Sources */,
26EC14D41D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */,
26EC14E81D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */,
263993BE1CE90EED004A6E93 /* File.swift in Sources */,
26B30BB81D2BC2E4004D4AB5 /* Scope.swift in Sources */,
269B47601D3538E90042D137 /* SlackKit.swift in Sources */,
2678B5991D3151CD00CE521A /* WebhookRequest.swift in Sources */,
26EC14FB1D1F355A00FD3A53 /* Action.swift in Sources */,
26EC14D71D1EF17400FD3A53 /* Edited.swift in Sources */,
263993BF1CE90EED004A6E93 /* WebAPI.swift in Sources */,
2678B5961D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26EC14E31D1EF18700FD3A53 /* Reaction.swift in Sources */,
26EC14EB1D1EF18F00FD3A53 /* Topic.swift in Sources */,
26B30BC51D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */,
263993C11CE90EED004A6E93 /* Message.swift in Sources */,
263993C21CE90EED004A6E93 /* Team.swift in Sources */,
269B47681D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */,
2678B5971D3151C600CE521A /* MessageActionRequest.swift in Sources */,
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */,
269B47C81D3AE25B0042D137 /* MessageActionServer.swift in Sources */,
263993C51CE90EED004A6E93 /* SlackError.swift in Sources */,
26EC14DC1D1EF17E00FD3A53 /* History.swift in Sources */,
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */,
26B30BC91D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
269B47641D3544240042D137 /* IncomingWebhook.swift in Sources */,
26EC14CB1D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C31D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */,
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
26072A3A1BB48B3B00CD650C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
26072A3B1BB48B3B00CD650C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
26072A3D1BB48B3B00CD650C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
26072A3E1BB48B3B00CD650C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = Release;
};
263993B01CE90EE0004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
263993B11CE90EE0004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = Release;
};
263993CF1CE90EED004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
263993D01CE90EED004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
26072A2E1BB48B3A00CD650C /* Build configuration list for PBXProject "SlackKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
26072A3A1BB48B3B00CD650C /* Debug */,
26072A3B1BB48B3B00CD650C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit OS X" */ = {
isa = XCConfigurationList;
buildConfigurations = (
26072A3D1BB48B3B00CD650C /* Debug */,
26072A3E1BB48B3B00CD650C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993B01CE90EE0004A6E93 /* Debug */,
263993B11CE90EE0004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993CF1CE90EED004A6E93 /* Debug */,
263993D01CE90EED004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 26072A2B1BB48B3A00CD650C /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:SlackRTMKit.xcodeproj">
</FileRef>
</Workspace>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.0</string>
<string>1.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+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"
}
+4 -4
View File
@@ -41,7 +41,7 @@ public struct Attachment {
public let footer: String?
public let footerIcon: String?
public let ts: Int?
internal init(attachment: [String: Any]?) {
fallback = attachment?["fallback"] as? String
callbackID = attachment?["callback_id"] as? String
@@ -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"
}
-47
View File
@@ -1,47 +0,0 @@
//
// AuthorizeRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct AuthorizeRequest {
let clientID: String
let scope: [Scope]
let redirectURI: String
let state: String
let team: String?
var parameters: [String: Any] {
var json = [String : Any]()
json["scope"] = scope.map({$0.rawValue}).joined(separator: ",")
json["state"] = state
json["team"] = team
return json
}
init(clientID: String, scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil) {
self.clientID = clientID
self.scope = scope
self.redirectURI = redirectURI
self.state = state
self.team = team
}
}
-36
View File
@@ -1,36 +0,0 @@
//
// AuthorizeResponse.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct AuthorizeResponse {
let code: String
let state: String
init?(queryParameters: [(String, String)]) {
guard let code = queryParameters.first?.1, let state = queryParameters.last?.1 else {
return nil
}
self.code = code
self.state = state
}
}
+1 -1
View File
@@ -70,7 +70,7 @@ public struct Channel {
unreadCountDisplay = channel?["unread_count_display"] as? Int
hasPins = channel?["has_pins"] as? Bool
members = channel?["members"] as? [String]
if let latestMesssageDictionary = channel?["latest"] as? [String: Any] {
latest = Message(dictionary: latestMesssageDictionary)
} else {
+70 -71
View File
@@ -21,141 +21,140 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal extension Client {
func dispatch(_ event: [String: Any]) {
let event = Event(event: event)
guard let type = event.type else {
return
}
internal extension SlackClient {
func dispatch(_ anEvent: [String: Any]) {
let event = Event(anEvent)
let type = event.type ?? .unknown
switch type {
case .Hello:
case .hello:
connected = true
pingRTMServer()
connectionEventsDelegate?.connected(self)
case .Ok:
case .ok:
messageSent(event)
case .Message:
case .message:
if (event.subtype != nil) {
messageDispatcher(event)
} else {
messageReceived(event)
}
case .UserTyping:
case .userTyping:
userTyping(event)
case .ChannelMarked, .IMMarked, .GroupMarked:
case .channelMarked, .imMarked, .groupMarked:
channelMarked(event)
case .ChannelCreated, .IMCreated:
case .channelCreated, .imCreated:
channelCreated(event)
case .ChannelJoined, .GroupJoined:
case .channelJoined, .groupJoined:
channelJoined(event)
case .ChannelLeft, .GroupLeft:
case .channelLeft, .groupLeft:
channelLeft(event)
case .ChannelDeleted:
case .channelDeleted:
channelDeleted(event)
case .ChannelRenamed, .GroupRename:
case .channelRenamed, .groupRename:
channelRenamed(event)
case .ChannelArchive, .GroupArchive:
case .channelArchive, .groupArchive:
channelArchived(event, archived: true)
case .ChannelUnarchive, .GroupUnarchive:
case .channelUnarchive, .groupUnarchive:
channelArchived(event, archived: false)
case .ChannelHistoryChanged, .IMHistoryChanged, .GroupHistoryChanged:
case .channelHistoryChanged, .imHistoryChanged, .groupHistoryChanged:
channelHistoryChanged(event)
case .DNDUpdated:
case .dndUpdated:
doNotDisturbUpdated(event)
case .DNDUpatedUser:
case .dndUpatedUser:
doNotDisturbUserUpdated(event)
case .IMOpen, .GroupOpen:
case .imOpen, .groupOpen:
open(event, open: true)
case .IMClose, .GroupClose:
case .imClose, .groupClose:
open(event, open: false)
case .FileCreated:
case .fileCreated:
processFile(event)
case .FileShared:
case .fileShared:
processFile(event)
case .FileUnshared:
case .fileUnshared:
processFile(event)
case .FilePublic:
case .filePublic:
processFile(event)
case .FilePrivate:
case .filePrivate:
filePrivate(event)
case .FileChanged:
case .fileChanged:
processFile(event)
case .FileDeleted:
case .fileDeleted:
deleteFile(event)
case .FileCommentAdded:
case .fileCommentAdded:
fileCommentAdded(event)
case .FileCommentEdited:
case .fileCommentEdited:
fileCommentEdited(event)
case .FileCommentDeleted:
case .fileCommentDeleted:
fileCommentDeleted(event)
case .PinAdded:
case .pinAdded:
pinAdded(event)
case .PinRemoved:
case .pinRemoved:
pinRemoved(event)
case .Pong:
case .pong:
pong(event)
case .PresenceChange:
case .presenceChange:
presenceChange(event)
case .ManualPresenceChange:
case .manualPresenceChange:
manualPresenceChange(event)
case .PrefChange:
case .prefChange:
changePreference(event)
case .UserChange:
case .userChange:
userChange(event)
case .TeamJoin:
case .teamJoin:
teamJoin(event)
case .StarAdded:
case .starAdded:
itemStarred(event, star: true)
case .StarRemoved:
case .starRemoved:
itemStarred(event, star: false)
case .ReactionAdded:
case .reactionAdded:
addedReaction(event)
case .ReactionRemoved:
case .reactionRemoved:
removedReaction(event)
case .EmojiChanged:
case .emojiChanged:
emojiChanged(event)
case .CommandsChanged:
// This functionality is only used by our web client.
// The other APIs required to support slash command metadata are currently unstable.
case .commandsChanged:
// This functionality is only used by our web client.
// The other APIs required to support slash command metadata are currently unstable.
// Until they are released other clients should ignore this event.
break
case .TeamPlanChange:
case .teamPlanChange:
teamPlanChange(event)
case .TeamPrefChange:
case .teamPrefChange:
teamPreferenceChange(event)
case .TeamRename:
case .teamRename:
teamNameChange(event)
case .TeamDomainChange:
case .teamDomainChange:
teamDomainChange(event)
case .EmailDomainChange:
case .emailDomainChange:
emailDomainChange(event)
case .TeamProfileChange:
case .teamProfileChange:
teamProfileChange(event)
case .TeamProfileDelete:
case .teamProfileDelete:
teamProfileDeleted(event)
case .TeamProfileReorder:
case .teamProfileReorder:
teamProfileReordered(event)
case .BotAdded:
case .botAdded:
bot(event)
case .BotChanged:
case .botChanged:
bot(event)
case .AccountsChanged:
case .accountsChanged:
// The accounts_changed event is used by our web client to maintain a list of logged-in accounts.
// Other clients should ignore this event.
break
case .TeamMigrationStarted:
connect(options: options ?? ClientOptions())
case .ReconnectURL:
case .teamMigrationStarted:
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
case .reconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
case .SubteamCreated, .SubteamUpdated:
case .subteamCreated, .subteamUpdated:
subteam(event)
case .SubteamSelfAdded:
case .subteamSelfAdded:
subteamAddedSelf(event)
case.SubteamSelfRemoved:
case .subteamSelfRemoved:
subteamRemovedSelf(event)
case .Error:
print("Error: \(event)")
case .unknown:
print("Unknown event of type: \(anEvent["type"] ?? "No Type Information")")
}
}
@@ -164,9 +163,9 @@ internal extension Client {
return
}
switch subtype {
case .MessageChanged:
case .messageChanged:
messageChanged(event)
case .MessageDeleted:
case .messageDeleted:
messageDeleted(event)
default:
messageReceived(event)
+21 -20
View File
@@ -22,9 +22,10 @@
// THE SOFTWARE.
import Foundation
import Dispatch
internal extension Client {
internal extension SlackClient {
//MARK: - Pong
func pong(_ event: Event) {
pong = event.replyTo
@@ -73,20 +74,20 @@ internal extension Client {
func userTyping(_ event: Event) {
guard let channel = event.channel, let channelID = channel.id, let user = event.user, let userID = user.id ,
channels.index(forKey: channelID) != nil && !channels[channelID]!.usersTyping.contains(userID) else {
return
return
}
channels[channelID]?.usersTyping.append(userID)
channelEventsDelegate?.userTypingIn(channel, user: user, client: self)
let timeout = DispatchTime.now() + Double(Int64(5.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
let timeout = DispatchTime.now() + Double(Int64(5.0 * Double(CLOCKS_PER_SEC))) / Double(CLOCKS_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: timeout, execute: {
if let index = self.channels[channelID]?.usersTyping.index(of: userID) {
self.channels[channelID]?.usersTyping.remove(at: index)
}
})
}
func channelMarked(_ event: Event) {
guard let channel = event.channel, let id = channel.id, let timestamp = event.ts else {
return
@@ -198,7 +199,7 @@ internal extension Client {
files[id]?.comments[commentID] = comment
}
}
files[id] = file
fileEventsDelegate?.processed(file, client: self)
}
@@ -264,13 +265,13 @@ internal extension Client {
guard let id = event.channelID, let item = event.item else {
return
}
if let pins = channels[id]?.pinnedItems.filter({$0 != item}) {
channels[id]?.pinnedItems = pins
}
pinEventsDelegate?.unpinned(item, channel: channels[id], client: self)
}
//MARK: - Stars
func itemStarred(_ event: Event, star: Bool) {
guard let item = event.item, let type = item.type else {
@@ -286,7 +287,7 @@ internal extension Client {
default:
break
}
starEventsDelegate?.starred(item, starred: star, self)
}
@@ -346,15 +347,15 @@ internal extension Client {
default:
break
}
reactionEventsDelegate?.added(reaction, item: item, itemUser: itemUser, client: self)
}
func removedReaction(_ event: Event) {
guard let item = event.item, let type = item.type, let key = event.reaction, let userID = event.user?.id, let itemUser = event.itemUser else {
return
}
switch type {
case "message":
guard let channel = item.channel, let ts = item.ts, let message = channels[channel]?.messages[ts] else {
@@ -374,10 +375,10 @@ internal extension Client {
default:
break
}
reactionEventsDelegate?.removed(key, item: item, itemUser: itemUser, client: self)
}
//MARK: - Preferences
func changePreference(_ event: Event) {
guard let name = event.name else {
@@ -512,7 +513,7 @@ internal extension Client {
guard let profile = event.profile else {
return
}
for user in users {
for key in profile.fields.keys {
users[user.0]?.profile?.customProfile?.fields[key]?.updateProfileField(profile.fields[key])
@@ -526,7 +527,7 @@ internal extension Client {
guard let profile = event.profile else {
return
}
for user in users {
if let id = profile.fields.first?.0 {
users[user.0]?.profile?.customProfile?.fields[id] = nil
@@ -540,13 +541,13 @@ internal extension Client {
guard let profile = event.profile else {
return
}
for user in users {
for key in profile.fields.keys {
users[user.0]?.profile?.customProfile?.fields[key]?.ordering = profile.fields[key]?.ordering
}
}
teamProfileEventsDelegate?.reordered(profile, client: self)
}
+5 -5
View File
@@ -26,7 +26,7 @@ public enum ClientError: Error {
case userDoesNotExist
}
public extension Client {
public extension SlackClient {
//MARK: - User & Channel
public func getChannelIDWith(name: String) throws -> String {
@@ -35,24 +35,24 @@ public extension Client {
}
return id
}
public func getUserIDWith(name: String) throws -> String {
guard let id = users.filter({$0.1.name == strip(string:name)}).first?.0 else {
throw ClientError.userDoesNotExist
}
return id
}
public func getImIDForUserWith(id: String, success: @escaping (_ imID: String?)->Void, failure: @escaping (SlackError)->Void) {
let ims = channels.filter{$0.1.isIM == true}
let channel = ims.filter{$0.1.user == id}.first
if let channel = channel {
success(channel.0)
} else {
webAPI.openIM(id, success: success, failure: failure)
webAPI.openIM(userID: id, success: success, failure: failure)
}
}
//MARK: - Utilities
internal func strip(string: String) -> String {
var strippedString = string
+144 -111
View File
@@ -22,11 +22,13 @@
// THE SOFTWARE.
import Foundation
import Starscream
import Venice
import WebSocketClient
public final class Client: WebSocketDelegate {
public class SlackClient {
internal(set) public var connected = false
internal(set) public var authenticated = false
internal(set) public var authenticatedUser: User?
internal(set) public var team: Team?
@@ -37,18 +39,6 @@ public final class Client: WebSocketDelegate {
internal(set) public var files = [String: File]()
internal(set) public var sentMessages = [String: Message]()
public var token = "xoxp-SLACK_AUTH_TOKEN"
public var webAPI: WebAPI {
return WebAPI(token: token)
}
internal var webSocket: WebSocket?
fileprivate let pingPongQueue = DispatchQueue(label: "com.launchsoft.SlackKit")
internal var ping: Double?
internal var pong: Double?
internal var options: ClientOptions?
//MARK: - Delegates
public weak var connectionEventsDelegate: ConnectionEventsDelegate?
public weak var slackEventsDelegate: SlackEventsDelegate?
@@ -63,56 +53,84 @@ public final class Client: WebSocketDelegate {
public weak var teamEventsDelegate: TeamEventsDelegate?
public weak var subteamEventsDelegate: SubteamEventsDelegate?
public weak var teamProfileEventsDelegate: TeamProfileEventsDelegate?
// If you already have an API token
internal init(apiToken: String) {
self.token = apiToken
}
public func setAuthToken(_ token: String) {
internal var token = "SLACK_AUTH_TOKEN"
public func setAuthToken(token: String) {
self.token = token
}
public func connect(options: ClientOptions = ClientOptions()) {
self.options = options
webAPI.rtmStart(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
}
public var webAPI: SlackWebAPI {
return SlackWebAPI(token: token)
}
internal var client: WebSocketClient?
internal var socket: WebSocket?
internal var ping: Double?
internal var pong: Double?
internal var pingInterval: Double = 30
internal var timeout: Double = 300
internal var reconnect: Bool = false
required public init(apiToken: String) {
self.token = apiToken
}
public func connect(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, pingInterval: Double = 30, timeout: Double = 300, reconnect: Bool = false) {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
webAPI.rtmStart(simpleLatest: simpleLatest, noUnreads: noUnreads, mpimAware: mpimAware, success: { (response) in
self.initialSetup(JSON: response)
self.webSocket = WebSocket(url: url)
self.webSocket?.delegate = self
self.webSocket?.connect()
if let socketURL = response["url"] as? String, let url = URL(string: socketURL) {
do {
self.client = try WebSocketClient(url: url, didConnect: { (socket) in
self.setupSocket(socket)
})
try self.client?.connect()
} catch let error {
print("WebSocket client could not connect: \(error)")
}
}
}, failure: {(error) in
self.connectionEventsDelegate?.connectionFailed(self, error: error)
print("rtm.start failed with error: \(error)")
})
}
public func disconnect() {
webSocket?.disconnect()
_ = try? socket?.close()
}
//MARK: - RTM Message send
public func send(message: String, channelID: String) {
guard connected else { return }
if let data = try? format(message: message, channel: channelID), let string = String(data: data, encoding: String.Encoding.utf8) {
webSocket?.write(string: string)
//MARK: - RTM message send
public func sendMessage(message: String, channelID: String) {
if connected {
if let data = formatMessageToSlackJsonString(message: message, channel: channelID) {
do {
try socket?.send(data.base64EncodedString())
} catch let error {
print("Message failed to send: \(error)")
}
}
}
}
fileprivate func format(message: String, channel: String) throws -> Data {
private func formatMessageToSlackJsonString(message: String, channel: String) -> Data? {
let json: [String: Any] = [
"id": Date().slackTimestamp,
"type": "message",
"channel": channel,
"text": message.slackFormatEscaping
]
addSentMessage(json)
return try JSONSerialization.data(withJSONObject: json, options: [])
do {
return try JSONSerialization.data(withJSONObject: json, options: [])
} catch {
return nil
}
}
fileprivate func addSentMessage(_ dictionary: [String: Any]) {
private func addSentMessage(_ dictionary: [String: Any]) {
var message = dictionary
guard let id = message["id"] as? NSNumber else {
return
@@ -124,51 +142,8 @@ public final class Client: WebSocketDelegate {
sentMessages[ts] = Message(dictionary: message)
}
//MARK: - RTM Ping
fileprivate 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 {
self.disconnect()
return
}
self.sendRTMPing()
self.pingRTMServerAt(interval: interval)
})
}
fileprivate func sendRTMPing() {
guard connected else {
return
}
let json: [String: Any] = [
"id": Date().slackTimestamp,
"type": "ping"
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
return
}
if let string = String(data: data, encoding: String.Encoding.utf8) {
ping = json["id"] as? Double
webSocket?.write(string: string)
}
}
fileprivate func timeoutCheck() -> Bool {
if let pong = pong, let ping = ping, let timeout = options?.timeout {
if pong - ping < timeout {
return true
} else {
return false
}
// Ping-pong or timeout not configured
} else {
return true
}
}
//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 +156,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 +197,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] {
@@ -232,32 +207,90 @@ public final class Client: WebSocketDelegate {
}
}
// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = options?.pingInterval {
pingRTMServerAt(interval: pingInterval)
//MARK: - RTM Ping
internal func pingRTMServer() {
co {
self.sendRTMPing()
nap(for: self.pingInterval.seconds)
guard self.connected && self.isConnectionTimedOut else {
self.disconnect()
return
}
self.pingRTMServer()
}
}
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
connected = false
webSocket = nil
authenticatedUser = nil
connectionEventsDelegate?.disconnected(self)
if let options = options, options.reconnect == true {
connect(options: options)
}
}
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
guard let data = text.data(using: String.Encoding.utf8) else {
private func sendRTMPing() {
guard connected else {
return
}
if let json = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)) as? [String: Any] {
dispatch(json)
let json: [String: Any] = [
"id": Date().slackTimestamp,
"type": "ping"
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
return
}
if let string = String(data: data, encoding: String.Encoding.utf8) {
ping = json["id"] as? Double
do {
try socket?.send(string)
} catch let error {
print("Failed to send ping with error: \(error)")
}
}
}
var isConnectionTimedOut: Bool {
if let pong = pong, let ping = ping {
if pong - ping < timeout {
return true
} else {
return false
}
} else {
return true
}
}
// MARK: - WebSocket
private func setupSocket(_ socket: WebSocket) {
socket.onText {(message) in
self.websocketDidReceive(message: message)
}
socket.onClose{ (code: CloseCode?, reason: String?) in
self.websocketDidDisconnect(closeCode: code, error: reason)
}
socket.onPing { (data) in try socket.pong() }
socket.onPong { (data) in try socket.ping() }
self.socket = socket
}
private func websocketDidReceive(message: String) {
do {
guard let message = message.data(using: .utf8) else {
print("Failed to decode message")
return
}
let json = try JSONSerialization.jsonObject(with: message, options: [])
if let event = json as? [String: Any] {
dispatch(event)
}
}
catch let error {
print("Failed to dispatch message: \(error)")
}
}
private func websocketDidDisconnect(closeCode: CloseCode?, error: String?) {
connected = false
authenticated = false
client = nil
socket = nil
authenticatedUser = nil
connectionEventsDelegate?.disconnected(self)
if reconnect == true {
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
}
}
public func websocketDidReceiveData(socket: WebSocket, data: Data) {}
}
-43
View File
@@ -1,43 +0,0 @@
//
// ClientOptions.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct ClientOptions {
let simpleLatest: Bool?
let noUnreads: Bool?
let mpimAware: Bool?
let pingInterval: TimeInterval?
let timeout: TimeInterval?
let reconnect: Bool?
public init(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, pingInterval: TimeInterval? = nil, timeout: TimeInterval? = nil, reconnect: Bool? = nil) {
self.simpleLatest = simpleLatest
self.noUnreads = noUnreads
self.mpimAware = mpimAware
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
}
}
+99 -100
View File
@@ -23,108 +23,107 @@
internal enum EventType: String {
case Hello = "hello"
case Message = "message"
case UserTyping = "user_typing"
case ChannelMarked = "channel_marked"
case ChannelCreated = "channel_created"
case ChannelJoined = "channel_joined"
case ChannelLeft = "channel_left"
case ChannelDeleted = "channel_deleted"
case ChannelRenamed = "channel_rename"
case ChannelArchive = "channel_archive"
case ChannelUnarchive = "channel_unarchive"
case ChannelHistoryChanged = "channel_history_changed"
case DNDUpdated = "dnd_updated"
case DNDUpatedUser = "dnd_updated_user"
case IMCreated = "im_created"
case IMOpen = "im_open"
case IMClose = "im_close"
case IMMarked = "im_marked"
case IMHistoryChanged = "im_history_changed"
case GroupJoined = "group_joined"
case GroupLeft = "group_left"
case GroupOpen = "group_open"
case GroupClose = "group_close"
case GroupArchive = "group_archive"
case GroupUnarchive = "group_unarchive"
case GroupRename = "group_rename"
case GroupMarked = "group_marked"
case GroupHistoryChanged = "group_history_changed"
case FileCreated = "file_created"
case FileShared = "file_shared"
case FileUnshared = "file_unshared"
case FilePublic = "file_public"
case FilePrivate = "file_private"
case FileChanged = "file_change"
case FileDeleted = "file_deleted"
case FileCommentAdded = "file_comment_added"
case FileCommentEdited = "file_comment_edited"
case FileCommentDeleted = "file_comment_deleted"
case PinAdded = "pin_added"
case PinRemoved = "pin_removed"
case Pong = "pong"
case PresenceChange = "presence_change"
case ManualPresenceChange = "manual_presence_change"
case PrefChange = "pref_change"
case UserChange = "user_change"
case TeamJoin = "team_join"
case StarAdded = "star_added"
case StarRemoved = "star_removed"
case ReactionAdded = "reaction_added"
case ReactionRemoved = "reaction_removed"
case EmojiChanged = "emoji_changed"
case CommandsChanged = "commands_changed"
case TeamPlanChange = "team_plan_change"
case TeamPrefChange = "team_pref_change"
case TeamRename = "team_rename"
case TeamDomainChange = "team_domain_change"
case EmailDomainChange = "email_domain_change"
case TeamProfileChange = "team_profile_change"
case TeamProfileDelete = "team_profile_delete"
case TeamProfileReorder = "team_profile_reorder"
case BotAdded = "bot_added"
case BotChanged = "bot_changed"
case AccountsChanged = "accounts_changed"
case TeamMigrationStarted = "team_migration_started"
case ReconnectURL = "reconnect_url"
case SubteamCreated = "subteam_created"
case SubteamUpdated = "subteam_updated"
case SubteamSelfAdded = "subteam_self_added"
case SubteamSelfRemoved = "subteam_self_removed"
case Ok = "ok"
case Error = "error"
case hello = "hello"
case message = "message"
case userTyping = "user_typing"
case channelMarked = "channel_marked"
case channelCreated = "channel_created"
case channelJoined = "channel_joined"
case channelLeft = "channel_left"
case channelDeleted = "channel_deleted"
case channelRenamed = "channel_rename"
case channelArchive = "channel_archive"
case channelUnarchive = "channel_unarchive"
case channelHistoryChanged = "channel_history_changed"
case dndUpdated = "dnd_updated"
case dndUpatedUser = "dnd_updated_user"
case imCreated = "im_created"
case imOpen = "im_open"
case imClose = "im_close"
case imMarked = "im_marked"
case imHistoryChanged = "im_history_changed"
case groupJoined = "group_joined"
case groupLeft = "group_left"
case groupOpen = "group_open"
case groupClose = "group_close"
case groupArchive = "group_archive"
case groupUnarchive = "group_unarchive"
case groupRename = "group_rename"
case groupMarked = "group_marked"
case groupHistoryChanged = "group_history_changed"
case fileCreated = "file_created"
case fileShared = "file_shared"
case fileUnshared = "file_unshared"
case filePublic = "file_public"
case filePrivate = "file_private"
case fileChanged = "file_change"
case fileDeleted = "file_deleted"
case fileCommentAdded = "file_comment_added"
case fileCommentEdited = "file_comment_edited"
case fileCommentDeleted = "file_comment_deleted"
case pinAdded = "pin_added"
case pinRemoved = "pin_removed"
case pong = "pong"
case presenceChange = "presence_change"
case manualPresenceChange = "manual_presence_change"
case prefChange = "pref_change"
case userChange = "user_change"
case teamJoin = "team_join"
case starAdded = "star_added"
case starRemoved = "star_removed"
case reactionAdded = "reaction_added"
case reactionRemoved = "reaction_removed"
case emojiChanged = "emoji_changed"
case commandsChanged = "commands_changed"
case teamPlanChange = "team_plan_change"
case teamPrefChange = "team_pref_change"
case teamRename = "team_rename"
case teamDomainChange = "team_domain_change"
case emailDomainChange = "email_domain_change"
case teamProfileChange = "team_profile_change"
case teamProfileDelete = "team_profile_delete"
case teamProfileReorder = "team_profile_reorder"
case botAdded = "bot_added"
case botChanged = "bot_changed"
case accountsChanged = "accounts_changed"
case teamMigrationStarted = "team_migration_started"
case reconnectURL = "reconnect_url"
case subteamCreated = "subteam_created"
case subteamUpdated = "subteam_updated"
case subteamSelfAdded = "subteam_self_added"
case subteamSelfRemoved = "subteam_self_removed"
case ok = "ok"
case unknown = "unknown"
}
internal enum MessageSubtype: String {
case BotMessage = "bot_message"
case MeMessage = "me_message"
case MessageChanged = "message_changed"
case MessageDeleted = "message_deleted"
case ChannelJoin = "channel_join"
case ChannelLeave = "channel_leave"
case ChannelTopic = "channel_topic"
case ChannelPurpose = "channel_purpose"
case ChannelName = "channel_name"
case ChannelArchive = "channel_archive"
case ChannelUnarchive = "channel_unarchive"
case GroupJoin = "group_join"
case GroupLeave = "group_leave"
case GroupTopic = "group_topic"
case GroupPurpose = "group_purpose"
case GroupName = "group_name"
case GroupArchive = "group_archive"
case GroupUnarchive = "group_unarchive"
case FileShare = "file_share"
case FileComment = "file_comment"
case FileMention = "file_mention"
case PinnedItem = "pinned_item"
case UnpinnedItem = "unpinned_item"
case botMessage = "bot_message"
case meMessage = "me_message"
case messageChanged = "message_changed"
case messageDeleted = "message_deleted"
case channelJoin = "channel_join"
case channelLeave = "channel_leave"
case channelTopic = "channel_topic"
case channelPurpose = "channel_purpose"
case channelName = "channel_name"
case channelArchive = "channel_archive"
case channelUnarchive = "channel_unarchive"
case groupJoin = "group_join"
case groupLeave = "group_leave"
case groupTopic = "group_topic"
case groupPurpose = "group_purpose"
case groupName = "group_name"
case groupArchive = "group_archive"
case groupUnarchive = "group_unarchive"
case fileShare = "file_share"
case fileComment = "file_comment"
case fileMention = "file_mention"
case pinnedItem = "pinned_item"
case unpinnedItem = "unpinned_item"
}
internal struct Event {
internal class Event {
let type: EventType?
let ts: String?
let subtype: String?
@@ -162,7 +161,7 @@ internal struct Event {
let subteamID: String?
var profile: CustomProfile?
init(event:[String: Any]) {
init(_ event:[String: Any]) {
type = EventType(rawValue: event["type"] as? String ?? "ok")
ts = event["ts"] as? String
subtype = event["subtype"] as? String
@@ -196,20 +195,20 @@ internal struct Event {
nestedMessage = Message(dictionary: event["message"] as? [String: Any])
profile = CustomProfile(profile: event["profile"] as? [String: Any])
file = File(id: event["file"] as? String)
// Comment, Channel, and User can come across as Strings or Dictionaries
if let commentDictionary = event["comment"] as? [String: Any] {
comment = Comment(comment: commentDictionary)
} else {
comment = Comment(id: event["comment"] as? String)
}
if let userDictionary = event["user"] as? [String: Any] {
user = User(user: userDictionary)
} else {
user = User(id: event["user"] as? String)
}
if let channelDictionary = event["channel"] as? [String: Any] {
channel = Channel(channel: channelDictionary)
} else {
+48 -48
View File
@@ -22,88 +22,88 @@
// THE SOFTWARE.
public protocol ConnectionEventsDelegate: class {
func connected(_ client: Client)
func disconnected(_ client: Client)
func connectionFailed(_ client: Client, error: SlackError)
func connected(_ client: SlackClient)
func disconnected(_ client: SlackClient)
func connectionFailed(_ client: SlackClient, error: SlackError)
}
public protocol MessageEventsDelegate: class {
func sent(_ message: Message, client: Client)
func received(_ message: Message, client: Client)
func changed(_ message: Message, client: Client)
func deleted(_ message: Message?, client: Client)
func sent(_ message: Message, client: SlackClient)
func received(_ message: Message, client: SlackClient)
func changed(_ message: Message, client: SlackClient)
func deleted(_ message: Message?, client: SlackClient)
}
public protocol ChannelEventsDelegate: class {
func userTypingIn(_ channel: Channel, user: User, client: Client)
func marked(_ channel: Channel, timestamp: String, client: Client)
func created(_ channel: Channel, client: Client)
func deleted(_ channel: Channel, client: Client)
func renamed(_ channel: Channel, client: Client)
func archived(_ channel: Channel, client: Client)
func historyChanged(_ channel: Channel, client: Client)
func joined(_ channel: Channel, client: Client)
func left(_ channel: Channel, client: Client)
func userTypingIn(_ channel: Channel, user: User, client: SlackClient)
func marked(_ channel: Channel, timestamp: String, client: SlackClient)
func created(_ channel: Channel, client: SlackClient)
func deleted(_ channel: Channel, client: SlackClient)
func renamed(_ channel: Channel, client: SlackClient)
func archived(_ channel: Channel, client: SlackClient)
func historyChanged(_ channel: Channel, client: SlackClient)
func joined(_ channel: Channel, client: SlackClient)
func left(_ channel: Channel, client: SlackClient)
}
public protocol DoNotDisturbEventsDelegate: class {
func updated(_ status: DoNotDisturbStatus, client: Client)
func userUpdated(_ status: DoNotDisturbStatus, user: User, client: Client)
func updated(_ status: DoNotDisturbStatus, client: SlackClient)
func userUpdated(_ status: DoNotDisturbStatus, user: User, client: SlackClient)
}
public protocol GroupEventsDelegate: class {
func opened(_ group: Channel, client: Client)
func opened(_ group: Channel, client: SlackClient)
}
public protocol FileEventsDelegate: class {
func processed(_ file: File, client: Client)
func madePrivate(_ file: File, client: Client)
func deleted(_ file: File, client: Client)
func commentAdded(_ file: File, comment: Comment, client: Client)
func commentEdited(_ file: File, comment: Comment, client: Client)
func commentDeleted(_ file: File, comment: Comment, client: Client)
func processed(_ file: File, client: SlackClient)
func madePrivate(_ file: File, client: SlackClient)
func deleted(_ file: File, client: SlackClient)
func commentAdded(_ file: File, comment: Comment, client: SlackClient)
func commentEdited(_ file: File, comment: Comment, client: SlackClient)
func commentDeleted(_ file: File, comment: Comment, client: SlackClient)
}
public protocol PinEventsDelegate: class {
func pinned(_ item: Item, channel: Channel?, client: Client)
func unpinned(_ item: Item, channel: Channel?, client: Client)
func pinned(_ item: Item, channel: Channel?, client: SlackClient)
func unpinned(_ item: Item, channel: Channel?, client: SlackClient)
}
public protocol StarEventsDelegate: class {
func starred(_ item: Item, starred: Bool, _ client: Client)
func starred(_ item: Item, starred: Bool, _ client: SlackClient)
}
public protocol ReactionEventsDelegate: class {
func added(_ reaction: String, item: Item, itemUser: String, client: Client)
func removed(_ reaction: String, item: Item, itemUser: String, client: Client)
func added(_ reaction: String, item: Item, itemUser: String, client: SlackClient)
func removed(_ reaction: String, item: Item, itemUser: String, client: SlackClient)
}
public protocol SlackEventsDelegate: class {
func preferenceChanged(_ preference: String, value: Any?, client: Client)
func userChanged(_ user: User, client: Client)
func presenceChanged(_ user: User, presence: String, client: Client)
func manualPresenceChanged(_ user: User, presence: String, client: Client)
func botEvent(_ bot: Bot, client: Client)
func preferenceChanged(_ preference: String, value: Any?, client: SlackClient)
func userChanged(_ user: User, client: SlackClient)
func presenceChanged(_ user: User, presence: String, client: SlackClient)
func manualPresenceChanged(_ user: User, presence: String, client: SlackClient)
func botEvent(_ bot: Bot, client: SlackClient)
}
public protocol TeamEventsDelegate: class {
func userJoined(_ user: User, client: Client)
func planChanged(_ plan: String, client: Client)
func preferencesChanged(_ preference: String, value: Any?, client: Client)
func nameChanged(_ name: String, client: Client)
func domainChanged(_ domain: String, client: Client)
func emailDomainChanged(_ domain: String, client: Client)
func emojiChanged(_ client: Client)
func userJoined(_ user: User, client: SlackClient)
func planChanged(_ plan: String, client: SlackClient)
func preferencesChanged(_ preference: String, value: Any?, client: SlackClient)
func nameChanged(_ name: String, client: SlackClient)
func domainChanged(_ domain: String, client: SlackClient)
func emailDomainChanged(_ domain: String, client: SlackClient)
func emojiChanged(_ client: SlackClient)
}
public protocol SubteamEventsDelegate: class {
func event(_ userGroup: UserGroup, client: Client)
func selfAdded(_ subteamID: String, client: Client)
func selfRemoved(_ subteamID: String, client: Client)
func event(_ userGroup: UserGroup, client: SlackClient)
func selfAdded(_ subteamID: String, client: SlackClient)
func selfRemoved(_ subteamID: String, client: SlackClient)
}
public protocol TeamProfileEventsDelegate: class {
func changed(_ profile: CustomProfile, client: Client)
func deleted(_ profile: CustomProfile, client: Client)
func reordered(_ profile: CustomProfile, client: Client)
func changed(_ profile: CustomProfile, client: SlackClient)
func deleted(_ profile: CustomProfile, client: SlackClient)
func reordered(_ profile: CustomProfile, client: SlackClient)
}
+4 -6
View File
@@ -24,7 +24,7 @@
import Foundation
public extension Date {
var slackTimestamp: Double {
return NSNumber(value: timeIntervalSince1970).doubleValue
}
@@ -41,18 +41,16 @@ internal extension String {
}
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) {
if let value = self[key] as? String, let encodedValue = value.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
requestString += "&\(key)=\(encodedValue)"
} else if let value = self[key] as? Int {
} else if let value = self[key] {
requestString += "&\(key)=\(value)"
}
}
return requestString
}
}
+2 -2
View File
@@ -22,7 +22,7 @@
// THE SOFTWARE.
public struct File: Equatable {
public let id: String?
public let created: Int?
public let name: String?
@@ -131,7 +131,7 @@ public struct File: Equatable {
stars = file?["num_stars"] as? Int
isStarred = file?["is_starred"] as? Bool
pinnedTo = file?["pinned_to"] as? [String]
reactions = Reaction.reactionsFromArray(file?["reactions"] as? [[String: Any]])
reactions = Reaction.reactionsFromArray(file?["reactions"] as? [[String: Any]])
}
internal init(id:String?) {
-71
View File
@@ -1,71 +0,0 @@
//
// IncomingWebhook.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct IncomingWebhook {
public let url: String?
public let channel: String?
public let configurationURL: String?
public let username: String?
public let iconEmoji: String?
public let iconURL: String?
internal init(webhook: [String: Any]?) {
url = webhook?["url"] as? String
channel = webhook?["channel"] as? String
configurationURL = webhook?["configuration_url"] as? String
username = webhook?["username"] as? String
iconEmoji = webhook?["icon_emoji"] as? String
iconURL = webhook?["icon_url"] as? String
}
public init(url: String, channel: String? = nil, username: String? = nil, iconEmoji: String? = nil, iconURL: String? = nil) {
self.url = url
self.channel = channel
self.username = username
self.iconEmoji = iconEmoji
self.iconURL = iconURL
self.configurationURL = nil
}
public func postMessage(_ response: Response, success: ((Bool)->Void)? = nil, failure: ((SlackError)->Void)? = nil) {
if let url = self.url, let data = try? JSONSerialization.data(withJSONObject: jsonBody(response.json), options: []) {
NetworkInterface().customRequest(url, data: data, success: { _ in
success?(true)
}, errorClosure: {(error) in
failure?(error)
})
}
}
fileprivate func jsonBody(_ response: [String: Any]) -> [String: Any] {
var json = response
json["channel"] = channel
json["username"] = username
json["icon_emoji"] = iconEmoji
json["icon_url"] = iconURL
return json
}
}
@@ -1,51 +0,0 @@
//
// MessageActionRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct MessageActionRequest: Request {
let action: Action?
let callbackID: String?
let team: Team?
let channel: Channel?
let user: User?
let actionTS: String?
let messageTS: String?
let attachmentID: String?
let token: String?
let originalMessage: Message?
let responseURL: String
init(response: [String: Any]?) {
action = (response?["actions"] as? [[String:Any]])?.map({Action(action: $0)}).first
callbackID = response?["callback_id"] as? String
team = Team(team: response?["team"] as? [String: Any])
channel = Channel(channel: response?["channel"] as? [String: Any])
user = User(user: response?["channel"] as? [String: Any])
actionTS = response?["action_ts"] as? String
messageTS = response?["message_ts"] as? String
attachmentID = response?["attachment_id"] as? String
token = response?["token"] as? String
originalMessage = Message(dictionary: response?["original_message"] as? [String: Any])
responseURL = response?["response_url"] as? String ?? ""
}
}
@@ -1,39 +0,0 @@
//
// MessageActionResponder.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct MessageActionResponder {
public var responses:[(Action, Response)]
public init(responses:[(Action, Response)]) {
self.responses = responses
}
internal func responseForRequest(_ request:MessageActionRequest) -> Reply? {
if let response = responses.filter({$0.0.name == request.action?.name}).first?.1 {
return Reply.json(response: response)
} else {
return nil
}
}
}
@@ -1,45 +0,0 @@
//
// MessageActionServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
open class MessageActionServer: Server {
internal let responder: MessageActionResponder
required public init(token: String, route: String, responder: MessageActionResponder) {
self.responder = responder
super.init(token: token)
addRoute(route)
}
internal func addRoute(_ route: String) {
http.POST["/\(route)"] = { request in
let payload = request.parseUrlencodedForm()
let actionRequest = MessageActionRequest(response: self.jsonFromRequest(payload[0].1))
if let reply = self.responder.responseForRequest(actionRequest), actionRequest.token == self.token {
return self.request(actionRequest, reply: reply)
} else {
return .badRequest(.text("Bad request."))
}
}
}
}
+84 -103
View File
@@ -22,132 +22,113 @@
// THE SOFTWARE.
import Foundation
import HTTPClient
import WebSocketClient
internal struct NetworkInterface {
private let apiUrl = "https://slack.com/api/"
private let client: HTTPClient.Client?
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)"
init() {
do {
self.client = try Client(url: URL(string: "https://slack.com")!)
} catch {
self.client = nil
}
}
internal func request(_ endpoint: Endpoint, token: String, parameters: [String: Any]?, successClosure: ([String: Any])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?token=\(token)"
if let params = parameters {
requestString += params.requestStringFromParameters
}
guard let url = URL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
return
}
let request = URLRequest(url:url)
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
self.handleResponse(data, response: response, internalError: internalError, successClosure: {(json) in
successClosure(json)
}, errorClosure: {(error) in
errorClosure(error)
})
}.resume()
}
internal func customRequest(_ url: String, data: Data, success: @escaping (Bool)->Void, errorClosure: @escaping (SlackError)->Void) {
guard let url = URL(string: url.removePercentEncoding()) else {
errorClosure(SlackError.ClientNetworkError)
return
}
var request = URLRequest(url:url)
request.httpMethod = "POST"
let contentType = "application/json"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.httpBody = data
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
if internalError == nil {
success(true)
} else {
errorClosure(SlackError.ClientNetworkError)
}
}.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
}
guard let url = URL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
return
}
var request = URLRequest(url:url)
request.httpMethod = "POST"
let boundaryConstant = randomBoundary()
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"
var requestBodyData: Data = Data()
requestBodyData.append(boundaryStart.data(using: String.Encoding.utf8)!)
requestBodyData.append(contentDispositionString.data(using: String.Encoding.utf8)!)
requestBodyData.append(contentTypeString.data(using: String.Encoding.utf8)!)
requestBodyData.append(data)
requestBodyData.append("\r\n".data(using: String.Encoding.utf8)!)
requestBodyData.append(boundaryEnd.data(using: String.Encoding.utf8)!)
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.httpBody = requestBodyData as Data
URLSession.shared.dataTask(with: request) {
(data, response, internalError) -> Void in
self.handleResponse(data, response: response, internalError: internalError, successClosure: {(json) in
successClosure(json)
}, errorClosure: {(error) in
errorClosure(error)
})
}.resume()
}
private func handleResponse(_ data: Data?, response:URLResponse?, internalError:Error?, successClosure: ([String: Any])->Void, errorClosure: (SlackError)->Void) {
guard let data = data, let response = response as? HTTPURLResponse else {
errorClosure(SlackError.ClientNetworkError)
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
errorClosure(SlackError.ClientJSONError)
return
}
switch response.statusCode {
case 200:
if (json["ok"] as! Bool == true) {
successClosure(json)
} else {
if let errorString = json["error"] as? String {
throw SlackError(rawValue: errorString) ?? .UnknownError
let contentNegotiation = ContentNegotiationMiddleware(mediaTypes: [.json, .urlEncodedForm], mode: .client)
var response = try client?.get(requestString, middleware: [contentNegotiation])
if let buffer = try response?.body.becomeBuffer(deadline: 3.seconds.fromNow()) {
let data = Data(bytes: buffer.bytes)
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let result = json as? [String: Any] {
if (result["ok"] as? Bool == true) {
successClosure(result)
} else {
throw SlackError.UnknownError
if let errorString = result["error"] as? String {
throw SlackError(rawValue: errorString) ?? SlackError.unknownError
} else {
throw SlackError.unknownError
}
}
}
case 429:
throw SlackError.TooManyRequests
default:
throw SlackError.ClientNetworkError
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
errorClosure(SlackError.unknownError)
}
}
}
internal func uploadRequest(token: String, data: Data, parameters: [String: Any]?, successClosure: ([String: Any])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(Endpoint.filesUpload.rawValue)?token=\(token)"
if let params = parameters {
requestString = requestString + params.requestStringFromParameters
}
let boundaryConstant = randomBoundary()
let boundaryStart = "--\(boundaryConstant)\r\n"
let boundaryEnd = "\r\n--\(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"
guard let boundaryStartData = boundaryStart.data(using: .utf8), let dispositionData = contentDispositionString.data(using: .utf8), let contentTypeData = contentTypeString.data(using: .utf8), let boundaryEndData = boundaryEnd.data(using: .utf8) else {
errorClosure(SlackError.clientNetworkError)
return
}
var requestBodyData = Data()
requestBodyData.append(contentsOf: boundaryStartData)
requestBodyData.append(contentsOf: dispositionData)
requestBodyData.append(contentsOf: contentTypeData)
requestBodyData.append(contentsOf: data)
requestBodyData.append(contentsOf: boundaryEndData)
let header: Headers = ["Content-Type":"multipart/form-data; boundary=\(boundaryConstant)"]
do {
let body = Buffer([UInt8](requestBodyData))
var response = try client?.post(requestString, headers: header, body: body)
if let buffer = try response?.body.becomeBuffer(deadline: 3.seconds.fromNow()) {
let data = Data(bytes: buffer.bytes)
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let result = json as? [String: Any] {
if (result["ok"] as? Bool == true) {
successClosure(result)
} else {
if let errorString = result["error"] as? String {
throw SlackError(rawValue: errorString) ?? SlackError.unknownError
} else {
throw SlackError.unknownError
}
}
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.unknownError)
}
}
}
private func randomBoundary() -> String {
return String(format: "slackkit.boundary.%08x%08x", arc4random(), arc4random())
#if os(Linux)
return "slackkit.boundary.\(Int(random()))\(Int(random()))"
#else
return "slackkit.boundary.\(arc4random())\(arc4random())"
#endif
}
}
-44
View File
@@ -1,44 +0,0 @@
//
// OAuthResponse.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct OAuthResponse {
let accessToken: String?
let scope: [Scope]?
let userID: String?
let teamName: String?
let teamID: String?
let incomingWebhook: IncomingWebhook?
let bot: Bot?
init(response: [String: Any]?) {
accessToken = response?["access_token"] as? String
scope = (response?["scope"] as? String)?.components(separatedBy: ",").flatMap{Scope(rawValue:$0)}
userID = response?["user_id"] as? String
teamName = response?["team_name"] as? String
teamID = response?["team_id"] as? String
incomingWebhook = IncomingWebhook(webhook: response?["incoming_webhook"] as? [String: Any])
bot = Bot(botUser: response?["bot"] as? [String: Any])
}
}
-94
View File
@@ -1,94 +0,0 @@
//
// OAuthServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Swifter
internal protocol OAuthDelegate {
func userAuthed(_ response: OAuthResponse)
}
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?
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
self.clientSecret = clientSecret
self.state = state ?? "state"
self.redirectURI = redirectURI
self.delegate = delegate
oauthRoute()
start(port, forceIPV4: forceIPV4)
}
public func start(_ port: in_port_t = 8080, forceIPV4: Bool = false) {
do {
try http.start(port, forceIPv4: forceIPV4)
} catch let error as NSError {
print("Server failed to start with error: \(error)")
}
}
public func stop() {
http.stop()
}
fileprivate 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
self.delegate?.userAuthed(OAuthResponse(response: response))
}, failure: {(error) in
print("Authorization failed")
})
if let redirect = self.redirectURI {
return .movedPermanently(redirect)
}
return .ok(.text("Authentication successful."))
}
}
fileprivate func oauthURLRequest(_ authorize: AuthorizeRequest) -> URLRequest? {
var requestString = "\(oauthURL)?client_id=\(authorize.clientID)"
requestString += authorize.parameters.requestStringFromParameters
guard let url = URL(string: requestString) else {
return nil
}
return URLRequest(url:url)
}
public func authorizeRequest(_ scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil) -> URLRequest? {
let request = AuthorizeRequest(clientID: clientID, scope: scope, redirectURI: redirectURI, state: state, team: team)
return oauthURLRequest(request)
}
}
-43
View File
@@ -1,43 +0,0 @@
//
// Response.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Response {
let text: String
let responseType: ResponseType?
let attachments: [Attachment]?
public init(text: String, responseType: ResponseType? = nil, attachments: [Attachment]? = nil) {
self.responseType = responseType
self.text = text
self.attachments = attachments
}
internal var json: [String: Any] {
var json = [String : Any]()
json["text"] = text
json["response_type"] = responseType?.rawValue
json["attachments"] = attachments?.map({$0.dictionary})
return json
}
}
-71
View File
@@ -1,71 +0,0 @@
//
// Scope.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public enum Scope: String {
case ChannelsHistory = "channels:history"
case ChannelsRead = "channels:read"
case ChannelsWrite = "channels:write"
case ChatWriteBot = "chat:write:bot"
case ChatWriteUser = "chat:write:user"
case DNDRead = "dnd:read"
case DNDWrite = "dnd:write"
case EmojiRead = "emoji:read"
case FilesRead = "files:read"
case FilesWriteUser = "files:write:user"
case GroupsHistory = "groups:history"
case GroupsRead = "groups:read"
case GroupsWrite = "groups:write"
case IdentityBasic = "identity.basic"
case IMHistory = "im:history"
case IMRead = "im:read"
case IMWrite = "im:write"
case MPIMHistory = "mpim:history"
case MPIMRead = "mpim:read"
case MPIMWrite = "mpim:write"
case PinsRead = "pins:read"
case PinsWrite = "pins:write"
case ReactionsRead = "reactions:read"
case ReactionsWrite = "reactions:write"
case RemindersRead = "reminders:read"
case RemindersWrite = "reminders:write"
case SearchRead = "search:read"
case StarsRead = "stars:read"
case StarsWrite = "stars:write"
case TeamRead = "team:read"
case UserGroupsRead = "usergroups:read"
case UserGroupsWrite = "usergroups:write"
case UserProfilesRead = "user.profiles:read"
case UserProfilesWrite = "user.profiles:write"
case UsersRead = "users:read"
case UsersWrite = "users:write"
case IncomingWebhook = "incoming-webhook"
case Commands = "commands"
case Bot = "bot"
case Identify = "identify"
case Client = "client"
case Admin = "admin"
//Deprecated
case Read = "read"
case Post = "post"
}
-90
View File
@@ -1,90 +0,0 @@
//
// Server.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Swifter
internal enum Reply {
case json(response: Response)
case text(body: String)
case badRequest
}
internal protocol Request {
var responseURL: String { get }
}
open class Server {
internal let http = HttpServer()
internal let token: String
internal init(token: String) {
self.token = token
}
open func start(_ port: in_port_t = 8080, forceIPV4: Bool = false) {
do {
try http.start(port, forceIPv4: forceIPV4)
} catch let error as NSError {
print("Server failed to start with error: \(error)")
}
}
open func stop() {
http.stop()
}
internal func request(_ request:Request, reply: Reply) -> HttpResponse {
switch reply {
case .text(let body):
return .ok(.text(body))
case .json(let response):
return .ok(.json(response.json))
case .badRequest:
return .badRequest(.text("Bad request."))
}
}
internal func dictionaryFromRequest(_ body: [UInt8]) -> [String: Any]? {
let string = String(data: Data(bytes: UnsafePointer<UInt8>(body), count: body.count), encoding: String.Encoding.utf8)
if let body = string?.components(separatedBy: "&") {
var dict: [String: Any] = [:]
for argument in body {
let kv = argument.components(separatedBy: "=")
if let key = kv.first, let value = kv.last {
dict[key] = value as Any?
}
}
return dict
}
return nil
}
internal func jsonFromRequest(_ string: String) -> [String: Any]? {
guard let data = string.data(using: String.Encoding.utf8) else {
return nil
}
return (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] ?? nil
}
}
+94 -95
View File
@@ -22,101 +22,100 @@
// THE SOFTWARE.
public enum SlackError: String, Error {
case AccountInactive = "account_inactive"
case AlreadyArchived = "already_archived"
case AlreadyInChannel = "already_in_channel"
case AlreadyPinned = "already_pinned"
case AlreadyReacted = "already_reacted"
case AlreadyStarred = "already_starred"
case BadClientSecret = "bad_client_secret"
case BadRedirectURI = "bad_redirect_uri"
case BadTimeStamp = "bad_timestamp"
case CantArchiveGeneral = "cant_archive_general"
case CantDelete = "cant_delete"
case CantDeleteFile = "cant_delete_file"
case CantDeleteMessage = "cant_delete_message"
case CantInvite = "cant_invite"
case CantInviteSelf = "cant_invite_self"
case CantKickFromGeneral = "cant_kick_from_general"
case CantKickFromLastChannel = "cant_kick_from_last_channel"
case CantKickSelf = "cant_kick_self"
case CantLeaveGeneral = "cant_leave_general"
case CantLeaveLastChannel = "cant_leave_last_channel"
case CantUpdateMessage = "cant_update_message"
case ChannelNotFound = "channel_not_found"
case ComplianceExportsPreventDeletion = "compliance_exports_prevent_deletion"
case EditWindowClosed = "edit_window_closed"
case FileCommentNotFound = "file_comment_not_found"
case FileDeleted = "file_deleted"
case FileNotFound = "file_not_found"
case FileNotShared = "file_not_shared"
case GroupContainsOthers = "group_contains_others"
case InvalidArgName = "invalid_arg_name"
case InvalidArrayArg = "invalid_array_arg"
case InvalidAuth = "invalid_auth"
case InvalidChannel = "invalid_channel"
case InvalidCharSet = "invalid_charset"
case InvalidClientID = "invalid_client_id"
case InvalidCode = "invalid_code"
case InvalidFormData = "invalid_form_data"
case InvalidName = "invalid_name"
case InvalidPostType = "invalid_post_type"
case InvalidPresence = "invalid_presence"
case InvalidTS = "invalid_timestamp"
case InvalidTSLatest = "invalid_ts_latest"
case InvalidTSOldest = "invalid_ts_oldest"
case IsArchived = "is_archived"
case LastMember = "last_member"
case LastRAChannel = "last_ra_channel"
case MessageNotFound = "message_not_found"
case MessageTooLong = "msg_too_long"
case MigrationInProgress = "migration_in_progress"
case MissingDuration = "missing_duration"
case MissingPostType = "missing_post_type"
case MissingScope = "missing_scope"
case NameTaken = "name_taken"
case NoChannel = "no_channel"
case NoComment = "no_comment"
case NoItemSpecified = "no_item_specified"
case NoReaction = "no_reaction"
case NoText = "no_text"
case NotArchived = "not_archived"
case NotAuthed = "not_authed"
case NotEnoughUsers = "not_enough_users"
case NotInChannel = "not_in_channel"
case NotInGroup = "not_in_group"
case NotPinned = "not_pinned"
case NotStarred = "not_starred"
case OverPaginationLimit = "over_pagination_limit"
case PaidOnly = "paid_only"
case PermissionDenied = "perimssion_denied"
case PostingToGeneralChannelDenied = "posting_to_general_channel_denied"
case RateLimited = "rate_limited"
case RequestTimeout = "request_timeout"
case RestrictedAction = "restricted_action"
case SnoozeEndFailed = "snooze_end_failed"
case SnoozeFailed = "snooze_failed"
case SnoozeNotActive = "snooze_not_active"
case TooLong = "too_long"
case TooManyEmoji = "too_many_emoji"
case TooManyReactions = "too_many_reactions"
case TooManyUsers = "too_many_users"
case UnknownError
case UnknownType = "unknown_type"
case UserDisabled = "user_disabled"
case UserDoesNotOwnChannel = "user_does_not_own_channel"
case UserIsBot = "user_is_bot"
case UserIsRestricted = "user_is_restricted"
case UserIsUltraRestricted = "user_is_ultra_restricted"
case UserListNotSupplied = "user_list_not_supplied"
case UserNotFound = "user_not_found"
case UserNotVisible = "user_not_visible"
case accountInactive = "account_inactive"
case alreadyArchived = "already_archived"
case alreadyInChannel = "already_in_channel"
case alreadyPinned = "already_pinned"
case alreadyReacted = "already_reacted"
case alreadyStarred = "already_starred"
case badClientSecret = "bad_client_secret"
case badRedirectURI = "bad_redirect_uri"
case badTimeStamp = "bad_timestamp"
case cantArchiveGeneral = "cant_archive_general"
case cantDelete = "cant_delete"
case cantDeleteFile = "cant_delete_file"
case cantDeleteMessage = "cant_delete_message"
case cantInvite = "cant_invite"
case cantInviteSelf = "cant_invite_self"
case cantKickFromGeneral = "cant_kick_from_general"
case cantKickFromLastChannel = "cant_kick_from_last_channel"
case cantKickSelf = "cant_kick_self"
case cantLeaveGeneral = "cant_leave_general"
case cantLeaveLastChannel = "cant_leave_last_channel"
case cantUpdateMessage = "cant_update_message"
case channelNotFound = "channel_not_found"
case complianceExportsPreventDeletion = "compliance_exports_prevent_deletion"
case editWindowClosed = "edit_window_closed"
case fileCommentNotFound = "file_comment_not_found"
case fileDeleted = "file_deleted"
case fileNotFound = "file_not_found"
case fileNotShared = "file_not_shared"
case groupContainsOthers = "group_contains_others"
case invalidArgName = "invalid_arg_name"
case invalidArrayArg = "invalid_array_arg"
case invalidAuth = "invalid_auth"
case invalidChannel = "invalid_channel"
case invalidCharSet = "invalid_charset"
case invalidClientID = "invalid_client_id"
case invalidCode = "invalid_code"
case invalidFormData = "invalid_form_data"
case invalidName = "invalid_name"
case invalidPostType = "invalid_post_type"
case invalidPresence = "invalid_presence"
case invalidTS = "invalid_timestamp"
case invalidTSLatest = "invalid_ts_latest"
case invalidTSOldest = "invalid_ts_oldest"
case isArchived = "is_archived"
case lastMember = "last_member"
case lastRAChannel = "last_ra_channel"
case messageNotFound = "message_not_found"
case messageTooLong = "msg_too_long"
case migrationInProgress = "migration_in_progress"
case missingDuration = "missing_duration"
case missingPostType = "missing_post_type"
case missingScope = "missing_scope"
case nameTaken = "name_taken"
case noChannel = "no_channel"
case noComment = "no_comment"
case noItemSpecified = "no_item_specified"
case noReaction = "no_reaction"
case noText = "no_text"
case notArchived = "not_archived"
case notAuthed = "not_authed"
case notEnoughUsers = "not_enough_users"
case notInChannel = "not_in_channel"
case notInGroup = "not_in_group"
case notPinned = "not_pinned"
case notStarred = "not_starred"
case overPaginationLimit = "over_pagination_limit"
case paidOnly = "paid_only"
case permissionDenied = "perimssion_denied"
case postingToGeneralChannelDenied = "posting_to_general_channel_denied"
case rateLimited = "rate_limited"
case requestTimeout = "request_timeout"
case restrictedAction = "restricted_action"
case snoozeEndFailed = "snooze_end_failed"
case snoozeFailed = "snooze_failed"
case snoozeNotActive = "snooze_not_active"
case tooLong = "too_long"
case tooManyEmoji = "too_many_emoji"
case tooManyReactions = "too_many_reactions"
case tooManyUsers = "too_many_users"
case unknownError
case unknownType = "unknown_type"
case userDisabled = "user_disabled"
case userDoesNotOwnChannel = "user_does_not_own_channel"
case userIsBot = "user_is_bot"
case userIsRestricted = "user_is_restricted"
case userIsUltraRestricted = "user_is_ultra_restricted"
case userListNotSupplied = "user_list_not_supplied"
case userNotFound = "user_not_found"
case userNotVisible = "user_not_visible"
// Client
case ClientNetworkError
case ClientJSONError
case ClientOAuthError
case clientNetworkError
case clientJSONError
case clientOAuthError
// HTTP
case TooManyRequests
case UnknownHTTPError
case tooManyRequests
case unknownHTTPError
}
-66
View File
@@ -1,66 +0,0 @@
//
// SlackKit.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public final class SlackKit: OAuthDelegate {
internal(set) public var oauth: OAuthServer?
internal(set) public var clients: [String: Client] = [:]
fileprivate let clientOptions: ClientOptions
// Initalization block
public var onClientInitalization: ((Client) -> Void)?
// If you already have an API token
public init(withAPIToken token: String, clientOptions: ClientOptions = ClientOptions()) {
self.clientOptions = clientOptions
let client = Client(apiToken: token)
DispatchQueue.main.async(execute: {
self.onClientInitalization?(client)
})
clients[token] = client
client.connect(options: self.clientOptions)
}
// If you're going to be receiving and/or initiating OAuth requests, provide a client ID and secret
public init(clientID: String, clientSecret: String, state: String? = nil, redirectURI: String? = nil, port:in_port_t = 8080, forceIPV4: Bool = false, clientOptions: ClientOptions = ClientOptions()) {
self.clientOptions = clientOptions
oauth = try? OAuthServer(clientID: clientID, clientSecret: clientSecret, state: state, redirectURI: redirectURI, port: port, forceIPV4: forceIPV4, delegate: self)
}
internal func userAuthed(_ response: OAuthResponse) {
// User auth
if let token = response.accessToken {
let client = Client(apiToken: token)
self.onClientInitalization?(client)
clients[token] = client
}
// Bot User
if let token = response.bot?.botToken {
let client = Client(apiToken: token)
self.onClientInitalization?(client)
clients[token] = client
client.connect(options: self.clientOptions)
}
}
}
+698
View File
@@ -0,0 +1,698 @@
//
// SlackWebAPI.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
internal enum Endpoint: String {
case apiTest = "api.test"
case authTest = "auth.test"
case channelsHistory = "channels.history"
case channelsInfo = "channels.info"
case channelsList = "channels.list"
case channelsMark = "channels.mark"
case channelsSetPurpose = "channels.setPurpose"
case channelsSetTopic = "channels.setTopic"
case chatDelete = "chat.delete"
case chatMeMessage = "chat.meMessage"
case chatPostMessage = "chat.postMessage"
case chatUpdate = "chat.update"
case dndInfo = "dnd.info"
case dndTeamInfo = "dnd.teamInfo"
case emojiList = "emoji.list"
case filesCommentsAdd = "files.comments.add"
case filesCommentsEdit = "files.comments.edit"
case filesCommentsDelete = "files.comments.delete"
case filesDelete = "files.delete"
case filesInfo = "files.info"
case filesUpload = "files.upload"
case groupsClose = "groups.close"
case groupsHistory = "groups.history"
case groupsInfo = "groups.info"
case groupsList = "groups.list"
case groupsMark = "groups.mark"
case groupsOpen = "groups.open"
case groupsSetPurpose = "groups.setPurpose"
case groupsSetTopic = "groups.setTopic"
case imClose = "im.close"
case imHistory = "im.history"
case imList = "im.list"
case imMark = "im.mark"
case imOpen = "im.open"
case mpimClose = "mpim.close"
case mpimHistory = "mpim.history"
case mpimList = "mpim.list"
case mpimMark = "mpim.mark"
case mpimOpen = "mpim.open"
case pinsAdd = "pins.add"
case pinsRemove = "pins.remove"
case reactionsAdd = "reactions.add"
case reactionsGet = "reactions.get"
case reactionsList = "reactions.list"
case reactionsRemove = "reactions.remove"
case rtmStart = "rtm.start"
case starsAdd = "stars.add"
case starsRemove = "stars.remove"
case teamInfo = "team.info"
case usersGetPresence = "users.getPresence"
case usersInfo = "users.info"
case usersList = "users.list"
case usersSetActive = "users.setActive"
case usersSetPresence = "users.setPresence"
}
public class SlackWebAPI {
public typealias FailureClosure = (_ error: SlackError)->Void
public enum InfoType: String {
case purpose = "purpose"
case topic = "topic"
}
public enum ParseMode: String {
case full = "full"
case none = "none"
}
public enum Presence: String {
case auto = "auto"
case away = "away"
}
private enum ChannelType: String {
case channel = "channel"
case group = "group"
case im = "im"
}
private let networkInterface: NetworkInterface
private let token: String
public init(token: String) {
self.networkInterface = NetworkInterface()
self.token = token
}
//MARK: - RTM
public func rtmStart(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, success: ((_ response: [String: Any])->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["simple_latest": simpleLatest, "no_unreads": noUnreads, "mpim_aware": mpimAware]
networkInterface.request(.rtmStart, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(response)
}) {(error) in
failure?(error)
}
}
//MARK: - Auth Test
public func authenticationTest(success: ((_ authenticated: Bool)->Void)?, failure: FailureClosure?) {
networkInterface.request(.authTest, token: token, parameters: nil, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Channels
public func channelHistory(id: String, latest: String = "\(Date().slackTimestamp)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History?)->Void)?, failure: FailureClosure?) {
history(.channelsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func channelInfo(id: String, success: ((_ channel: Channel)->Void)?, failure: FailureClosure?) {
info(.channelsInfo, type:.channel, id: id, success: {(channel) in
success?(channel)
}) {(error) in
failure?(error)
}
}
public func channelsList(excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.channelsList, type:.channel, excludeArchived: excludeArchived, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markChannel(channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.channelsMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
public func setChannelPurpose(channel: String, purpose: String, success: ((_ purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.channelsSetPurpose, type: .purpose, channel: channel, text: purpose, success: {(purposeSet) in
success?(purposeSet)
}) { (error) in
failure?(error)
}
}
public func setChannelTopic(channel: String, topic: String, success: ((_ topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.channelsSetTopic, type: .topic, channel: channel, text: topic, success: {(topicSet) in
success?(topicSet)
}) {(error) in
failure?(error)
}
}
//MARK: - Messaging
public func deleteMessage(channel: String, ts: String, success: ((_ deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, "ts": ts]
networkInterface.request(.chatDelete, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
public func sendMessage(channel: String, text: String, username: String? = nil, asUser: Bool? = nil, parse: ParseMode? = nil, linkNames: Bool? = nil, attachments: [Attachment?]? = nil, unfurlLinks: Bool? = nil, unfurlMedia: Bool? = nil, iconURL: String? = nil, iconEmoji: String? = nil, success: (((ts: String?, channel: String?))->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel":channel, "text":text.slackFormatEscaping, "as_user":asUser, "parse":parse?.rawValue, "link_names":linkNames, "unfurl_links":unfurlLinks, "unfurlMedia":unfurlMedia, "username":username, "attachments":encodeAttachments(attachments), "icon_url":iconURL, "icon_emoji":iconEmoji]
networkInterface.request(.chatPostMessage, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func sendMeMessage(_ channel: String, text: String, success: (((ts: String?, channel: String?))->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel": channel, "text": text.slackFormatEscaping]
networkInterface.request(.chatMeMessage, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func updateMessage(channel: String, ts: String, message: String, attachments: [Attachment?]? = nil, parse:ParseMode = .none, linkNames: Bool = false, success: ((_ updated: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping, "parse": parse.rawValue, "link_names": linkNames, "attachments":encodeAttachments(attachments)]
networkInterface.request(.chatUpdate, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Do Not Disturb
public func dndInfo(user: String? = nil, success: ((_ status: DoNotDisturbStatus)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["user": user]
networkInterface.request(.dndInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(DoNotDisturbStatus(status: response))
}) {(error) in
failure?(error)
}
}
public func dndTeamInfo(users: [String]? = nil, success: ((_ statuses: [String: DoNotDisturbStatus])->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["users":users?.joined(separator: ",")]
networkInterface.request(.dndTeamInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
guard let usersDictionary = response["users"] as? [String: Any] else {
success?([:])
return
}
success?(self.enumerateDNDStatuses(usersDictionary))
}) {(error) in
failure?(error)
}
}
//MARK: - Emoji
public func emojiList(success: ((_ emojiList: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(.emojiList, token: token, parameters: nil, successClosure: {(response) in
success?(response["emoji"] as? [String: Any])
}) {(error) in
failure?(error)
}
}
//MARK: - Files
public func deleteFile(fileID: String, success: ((_ deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["file":fileID]
networkInterface.request(.filesDelete, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
public func fileInfo(_ fileID: String, commentCount: Int = 100, totalPages: Int = 1, success: ((_ file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "count": commentCount, "totalPages": totalPages]
networkInterface.request(.filesInfo, token: token, parameters: parameters, successClosure: {(response) in
var file = File(file: response["file"] as? [String: Any])
(response["comments"] as? [[String: Any]])?.forEach { comment in
let comment = Comment(comment: comment)
if let id = comment.id {
file.comments[id] = comment
}
}
success?(file)
}) {(error) in
failure?(error)
}
}
public func uploadFile(_ file: Data, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((_ file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["filename": filename, "filetype": filetype, "title": title, "initial_comment": initialComment, "channels": channels?.joined(separator: ",")]
networkInterface.uploadRequest(token: token, data: file, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(File(file: response["file"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
//MARK: - File Comments
public func addFileComment(fileID: String, comment: String, success: ((_ comment: Comment)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file":fileID, "comment":comment.slackFormatEscaping]
networkInterface.request(.filesCommentsAdd, token: token, parameters: parameters, successClosure: {(response) in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func editFileComment(fileID: String, commentID: String, comment: String, success: ((_ comment: Comment)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file":fileID, "id":commentID, "comment":comment.slackFormatEscaping]
networkInterface.request(.filesCommentsEdit, token: token, parameters: parameters, successClosure: {(response) in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func deleteFileComment(fileID: String, commentID: String, success: ((_ deleted: Bool?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file":fileID, "id": commentID]
networkInterface.request(.filesCommentsDelete, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Groups
public func closeGroup(groupID: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.groupsClose, channelID: groupID, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func groupHistory(id: String, latest: String = "\(Date().slackTimestamp)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History?)->Void)?, failure: FailureClosure?) {
history(.groupsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func groupInfo(id: String, success: ((_ channel: Channel?)->Void)?, failure: FailureClosure?) {
info(.groupsInfo, type:.group, id: id, success: {(channel) in
success?(channel)
}) {(error) in
failure?(error)
}
}
public func groupsList(excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.groupsList, type:.group, excludeArchived: excludeArchived, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markGroup(channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.groupsMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
public func openGroup(channel: String, success: ((_ opened: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["channel":channel]
networkInterface.request(.groupsOpen, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
public func setGroupPurpose(channel: String, purpose: String, success: ((_ purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.groupsSetPurpose, type: .purpose, channel: channel, text: purpose, success: {(purposeSet) in
success?(purposeSet)
}) {(error) in
failure?(error)
}
}
public func setGroupTopic(channel: String, topic: String, success: ((_ topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.groupsSetTopic, type: .topic, channel: channel, text: topic, success: {(topicSet) in
success?(topicSet)
}) {(error) in
failure?(error)
}
}
//MARK: - IM
public func closeIM(channel: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.imClose, channelID: channel, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func imHistory(id: String, latest: String = "\(Date().slackTimestamp)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History?)->Void)?, failure: FailureClosure?) {
history(.imHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func imsList(excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.imList, type:.im, excludeArchived: excludeArchived, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markIM(channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.imMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
public func openIM(userID: String, success: ((_ imID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["user":userID]
networkInterface.request(.imOpen, token: token, parameters: parameters, successClosure: {(response) in
let group = response["channel"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) in
failure?(error)
}
}
//MARK: - MPIM
public func closeMPIM(channel: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.mpimClose, channelID: channel, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func mpimHistory(id: String, latest: String = "\(Date().slackTimestamp)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History?)->Void)?, failure: FailureClosure?) {
history(.mpimHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func mpimsList(excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.mpimList, type:.group, excludeArchived: excludeArchived, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markMPIM(channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.mpimMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
public func openMPIM(userIDs: [String], success: ((_ mpimID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["users":userIDs.joined(separator: ",")]
networkInterface.request(.mpimOpen, token: token, parameters: parameters, successClosure: {(response) in
let group = response["group"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) in
failure?(error)
}
}
//MARK: - Pins
public func pinItem(channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ pinned: Bool)->Void)?, failure: FailureClosure?) {
pin(.pinsAdd, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
public func unpinItem(channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ unpinned: Bool)->Void)?, failure: FailureClosure?) {
pin(.pinsRemove, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func pin(_ endpoint: Endpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel":channel, "file":file, "file_comment":fileComment, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Reactions
// One of file, file_comment, or the combination of channel and timestamp must be specified.
public func addReaction(name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ reacted: Bool)->Void)?, failure: FailureClosure?) {
react(.reactionsAdd, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
// One of file, file_comment, or the combination of channel and timestamp must be specified.
public func removeReaction(name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ unreacted: Bool)->Void)?, failure: FailureClosure?) {
react(.reactionsRemove, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func react(_ endpoint: Endpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["name":name, "file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Stars
// One of file, file_comment, channel, or the combination of channel and timestamp must be specified.
public func addStar(file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ starred: Bool)->Void)?, failure: FailureClosure?) {
star(.starsAdd, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
// One of file, file_comment, channel, or the combination of channel and timestamp must be specified.
public func removeStar(file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ unstarred: Bool)->Void)?, failure: FailureClosure?) {
star(.starsRemove, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func star(_ endpoint: Endpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Team
public func teamInfo(success: ((_ info: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(.teamInfo, token: token, parameters: nil, successClosure: {(response) in
success?(response["team"] as? [String: Any])
}) {(error) in
failure?(error)
}
}
//MARK: - Users
public func userPresence(user: String, success: ((_ presence: String?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user":user]
networkInterface.request(.usersGetPresence, token: token, parameters: parameters, successClosure: {(response) in
success?(response["presence"] as? String)
}) {(error) in
failure?(error)
}
}
public func userInfo(id: String, success: ((_ user: User)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user":id]
networkInterface.request(.usersInfo, token: token, parameters: parameters, successClosure: {(response) in
success?(User(user: response["user"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func usersList(includePresence: Bool = false, success: ((_ userList: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence":includePresence]
networkInterface.request(.usersList, token: token, parameters: parameters, successClosure: {(response) in
success?(response["members"] as? [[String: Any]])
}) {(error) in
failure?(error)
}
}
public func setUserActive(success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
networkInterface.request(.usersSetActive, token: token, parameters: nil, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
public func setUserPresence(presence: Presence, success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence":presence.rawValue]
networkInterface.request(.usersSetPresence, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Channel Utilities
private func close(_ endpoint: Endpoint, channelID: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel":channelID]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
private func history(_ endpoint: Endpoint, id: String, latest: String = "\(Date().slackTimestamp)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": id, "latest": latest, "oldest": oldest, "inclusive":inclusive, "count":count, "unreads":unreads]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(History(history: response))
}) {(error) in
failure?(error)
}
}
private func info(_ endpoint: Endpoint, type: ChannelType, id: String, success: ((_ channel: Channel)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": id]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(Channel(channel: response[type.rawValue] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
private func list(_ endpoint: Endpoint, type: ChannelType, excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["exclude_archived": excludeArchived]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(response[type.rawValue+"s"] as? [[String: Any]])
}) {(error) in
failure?(error)
}
}
private func mark(_ endpoint: Endpoint, channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, "ts": timestamp]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
private func setInfo(_ endpoint: Endpoint, type: InfoType, channel: String, text: String, success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, type.rawValue: text]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {(response) in
success?(true)
}) {(error) in
failure?(error)
}
}
//MARK: - Encode Attachments
private func encodeAttachments(_ attachments: [Attachment?]?) -> String? {
if let attachments = attachments {
var attachmentArray: [[String: Any]] = []
for attachment in attachments {
if let attachment = attachment {
attachmentArray.append(attachment.dictionary)
}
}
do {
let data = try JSONSerialization.data(withJSONObject: attachmentArray, options: [])
return String(data: data, encoding: String.Encoding.utf8)
} catch _ {
print("Error encoding attachments")
}
}
return nil
}
//MARK: - Filter Nil Parameters
internal 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
}
//MARK: - Enumerate Do Not Disturb Status
private func enumerateDNDStatuses(_ statuses: [String: Any]) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
for key in statuses.keys {
retVal[key] = DoNotDisturbStatus(status: statuses[key] as? [String: Any])
}
return retVal
}
}
-769
View File
@@ -1,769 +0,0 @@
//
// WebAPI.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
internal enum Endpoint: String {
case APITest = "api.test"
case AuthRevoke = "auth.revoke"
case AuthTest = "auth.test"
case ChannelsHistory = "channels.history"
case ChannelsInfo = "channels.info"
case ChannelsList = "channels.list"
case ChannelsMark = "channels.mark"
case ChannelsSetPurpose = "channels.setPurpose"
case ChannelsSetTopic = "channels.setTopic"
case ChatDelete = "chat.delete"
case ChatPostMessage = "chat.postMessage"
case ChatUpdate = "chat.update"
case DNDInfo = "dnd.info"
case DNDTeamInfo = "dnd.teamInfo"
case EmojiList = "emoji.list"
case FilesCommentsAdd = "files.comments.add"
case FilesCommentsEdit = "files.comments.edit"
case FilesCommentsDelete = "files.comments.delete"
case FilesDelete = "files.delete"
case FilesInfo = "files.info"
case FilesUpload = "files.upload"
case GroupsClose = "groups.close"
case GroupsHistory = "groups.history"
case GroupsInfo = "groups.info"
case GroupsList = "groups.list"
case GroupsMark = "groups.mark"
case GroupsOpen = "groups.open"
case GroupsSetPurpose = "groups.setPurpose"
case GroupsSetTopic = "groups.setTopic"
case IMClose = "im.close"
case IMHistory = "im.history"
case IMList = "im.list"
case IMMark = "im.mark"
case IMOpen = "im.open"
case MPIMClose = "mpim.close"
case MPIMHistory = "mpim.history"
case MPIMList = "mpim.list"
case MPIMMark = "mpim.mark"
case MPIMOpen = "mpim.open"
case OAuthAccess = "oauth.access"
case PinsAdd = "pins.add"
case PinsRemove = "pins.remove"
case ReactionsAdd = "reactions.add"
case ReactionsGet = "reactions.get"
case ReactionsList = "reactions.list"
case ReactionsRemove = "reactions.remove"
case RTMStart = "rtm.start"
case StarsAdd = "stars.add"
case StarsRemove = "stars.remove"
case TeamInfo = "team.info"
case UsersGetPresence = "users.getPresence"
case UsersInfo = "users.info"
case UsersList = "users.list"
case UsersSetActive = "users.setActive"
case UsersSetPresence = "users.setPresence"
}
public final class WebAPI {
public typealias FailureClosure = (_ error: SlackError)->Void
public enum InfoType: String {
case Purpose = "purpose"
case Topic = "topic"
}
public enum ParseMode: String {
case Full = "full"
case None = "none"
}
public enum Presence: String {
case Auto = "auto"
case Away = "away"
}
fileprivate enum ChannelType: String {
case Channel = "channel"
case Group = "group"
case IM = "im"
}
fileprivate let networkInterface: NetworkInterface
fileprivate let token: String
public init(token: String) {
self.networkInterface = NetworkInterface()
self.token = token
}
//MARK: - RTM
public func rtmStart(_ simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, success: ((_ response: [String: Any])->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["simple_latest": simpleLatest, "no_unreads": noUnreads, "mpim_aware": mpimAware]
networkInterface.request(.RTMStart, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Auth
public func authenticationTest(_ success: ((_ authenticated: Bool)->Void)?, failure: FailureClosure?) {
networkInterface.request(.AuthTest, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
public static func oauthAccess(_ clientID: String, clientSecret: String, code: String, redirectURI: String? = nil, success: ((_ response: [String: Any])->Void)?, failure: ((SlackError)->Void)?) {
let parameters: [String: Any?] = ["client_id": clientID, "client_secret": clientSecret, "code": code, "redirect_uri": redirectURI]
NetworkInterface().request(.OAuthAccess, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response)
}) {(error) -> Void in
failure?(error)
}
}
public static func oauthRevoke(_ token: String, test: Bool? = nil, success: ((_ revoked:Bool)->Void)?, failure: ((SlackError)->Void)?) {
let parameters: [String: Any?] = ["token": token, "test": test]
NetworkInterface().request(.AuthRevoke, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Channels
public func channelHistory(_ id: String, latest: String = "\(Date().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
history(.ChannelsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history)
}) {(error) -> Void in
failure?(error)
}
}
public func channelInfo(_ id: String, success: ((_ channel: Channel)->Void)?, failure: FailureClosure?) {
info(.ChannelsInfo, type:ChannelType.Channel, id: id, success: {
(channel) -> Void in
success?(channel)
}) { (error) -> Void in
failure?(error)
}
}
public func channelsList(_ excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.ChannelsList, type:ChannelType.Channel, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels)
}) {(error) -> Void in
failure?(error)
}
}
public func markChannel(_ channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.ChannelsMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(timestamp)
}) {(error) -> Void in
failure?(error)
}
}
public func setChannelPurpose(_ channel: String, purpose: String, success: ((_ purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.ChannelsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
(purposeSet) -> Void in
success?(purposeSet)
}) { (error) -> Void in
failure?(error)
}
}
public func setChannelTopic(_ channel: String, topic: String, success: ((_ topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.ChannelsSetTopic, type: .Topic, channel: channel, text: topic, success: {
(topicSet) -> Void in
success?(topicSet)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Messaging
public func deleteMessage(_ channel: String, ts: String, success: ((_ deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, "ts": ts]
networkInterface.request(.ChatDelete, token: token, parameters: parameters, successClosure: { (response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
public func sendMessage(_ channel: String, text: String, username: String? = nil, asUser: Bool? = nil, parse: ParseMode? = nil, linkNames: Bool? = nil, attachments: [Attachment?]? = nil, unfurlLinks: Bool? = nil, unfurlMedia: Bool? = nil, iconURL: String? = nil, iconEmoji: String? = nil, success: (((ts: String?, channel: String?))->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel": channel, "text": text.slackFormatEscaping, "as_user": asUser, "parse": parse?.rawValue, "link_names": linkNames, "unfurl_links": unfurlLinks, "unfurlMedia": unfurlMedia, "username": username, "icon_url": iconURL, "icon_emoji": iconEmoji, "attachments": encodeAttachments(attachments)]
networkInterface.request(.ChatPostMessage, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) -> Void in
failure?(error)
}
}
public func updateMessage(_ channel: String, ts: String, message: String, attachments: [Attachment?]? = nil, parse:ParseMode = .None, linkNames: Bool = false, success: ((_ updated: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping, "parse": parse.rawValue, "link_names": linkNames, "attachments": encodeAttachments(attachments)]
networkInterface.request(.ChatUpdate, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Do Not Disturb
public func dndInfo(_ user: String? = nil, success: ((_ status: DoNotDisturbStatus)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["user": user]
networkInterface.request(.DNDInfo, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(DoNotDisturbStatus(status: response))
}) {(error) -> Void in
failure?(error)
}
}
public func dndTeamInfo(_ users: [String]? = nil, success: ((_ statuses: [String: DoNotDisturbStatus])->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["users": users?.joined(separator: ",")]
networkInterface.request(.DNDTeamInfo, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
guard let usersDictionary = response["users"] as? [String: Any] else {
success?([:])
return
}
success?(self.enumerateDNDStatuses(usersDictionary))
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Emoji
public func emojiList(_ success: ((_ emojiList: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(.EmojiList, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(response["emoji"] as? [String: Any])
}) { (error) -> Void in
failure?(error)
}
}
//MARK: - Files
public func deleteFile(_ fileID: String, success: ((_ deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["file": fileID]
networkInterface.request(.FilesDelete, token: token, parameters: parameters as [String : Any]?, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
public func fileInfo(_ fileID: String, commentCount: Int = 100, totalPages: Int = 1, success: ((_ file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "count": commentCount, "totalPages": totalPages]
networkInterface.request(.FilesInfo, token: token, parameters: parameters, successClosure: {
(response) in
var file = File(file: response["file"] as? [String: Any])
(response["comments"] as? [[String: Any]])?.forEach { comment in
let comment = Comment(comment: comment)
if let id = comment.id {
file.comments[id] = comment
}
}
success?(file)
}) {(error) in
failure?(error)
}
}
public func uploadFile(_ file: Data, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((_ file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["file": file, "filename": filename, "filetype": filetype, "title": title, "initial_comment": initialComment, "channels": channels?.joined(separator: ",")]
networkInterface.uploadRequest(token, data: file, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(File(file: response["file"] as? [String: Any]))
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - File Comments
public func addFileComment(_ fileID: String, comment: String, success: ((_ comment: Comment)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "comment": comment.slackFormatEscaping]
networkInterface.request(.FilesCommentsAdd, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) -> Void in
failure?(error)
}
}
public func editFileComment(_ fileID: String, commentID: String, comment: String, success: ((_ comment: Comment)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "id": commentID, "comment": comment.slackFormatEscaping]
networkInterface.request(.FilesCommentsEdit, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) -> Void in
failure?(error)
}
}
public func deleteFileComment(_ fileID: String, commentID: String, success: ((_ deleted: Bool?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "id": commentID]
networkInterface.request(.FilesCommentsDelete, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Groups
public func closeGroup(_ groupID: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.GroupsClose, channelID: groupID, success: {
(closed) -> Void in
success?(closed)
}) {(error) -> Void in
failure?(error)
}
}
public func groupHistory(_ id: String, latest: String = "\(Date().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
history(.GroupsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history)
}) {(error) -> Void in
failure?(error)
}
}
public func groupInfo(_ id: String, success: ((_ channel: Channel)->Void)?, failure: FailureClosure?) {
info(.GroupsInfo, type:ChannelType.Group, id: id, success: {
(channel) -> Void in
success?(channel)
}) {(error) -> Void in
failure?(error)
}
}
public func groupsList(_ excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.GroupsList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels)
}) {(error) -> Void in
failure?(error)
}
}
public func markGroup(_ channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.GroupsMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(timestamp)
}) {(error) -> Void in
failure?(error)
}
}
public func openGroup(_ channel: String, success: ((_ opened: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["channel":channel]
networkInterface.request(.GroupsOpen, token: token, parameters: parameters as [String: Any]?, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
public func setGroupPurpose(_ channel: String, purpose: String, success: ((_ purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.GroupsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
(purposeSet) -> Void in
success?(purposeSet)
}) {(error) -> Void in
failure?(error)
}
}
public func setGroupTopic(_ channel: String, topic: String, success: ((_ topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.GroupsSetTopic, type: .Topic, channel: channel, text: topic, success: {
(topicSet) -> Void in
success?(topicSet)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - IM
public func closeIM(_ channel: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.IMClose, channelID: channel, success: {
(closed) -> Void in
success?(closed)
}) {(error) -> Void in
failure?(error)
}
}
public func imHistory(_ id: String, latest: String = "\(Date().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
history(.IMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history)
}) {(error) -> Void in
failure?(error)
}
}
public func imsList(_ excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.IMList, type:ChannelType.IM, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels)
}) {(error) -> Void in
failure?(error)
}
}
public func markIM(_ channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.IMMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(timestamp)
}) {(error) -> Void in
failure?(error)
}
}
public func openIM(_ userID: String, success: ((_ imID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["user": userID]
networkInterface.request(.IMOpen, token: token, parameters: parameters as [String: Any]?, successClosure: {
(response) -> Void in
let group = response["channel"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - MPIM
public func closeMPIM(_ channel: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
close(.MPIMClose, channelID: channel, success: {
(closed) -> Void in
success?(closed)
}) {(error) -> Void in
failure?(error)
}
}
public func mpimHistory(_ id: String, latest: String = "\(Date().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
history(.MPIMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history)
}) {(error) -> Void in
failure?(error)
}
}
public func mpimsList(_ excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
list(.MPIMList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels)
}) {(error) -> Void in
failure?(error)
}
}
public func markMPIM(_ channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
mark(.MPIMMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(timestamp)
}) {(error) -> Void in
failure?(error)
}
}
public func openMPIM(_ userIDs: [String], success: ((_ mpimID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["users": userIDs.joined(separator: ",")]
networkInterface.request(.MPIMOpen, token: token, parameters: parameters as [String: Any]?, successClosure: {
(response) -> Void in
let group = response["group"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Pins
public func pinItem(_ channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ pinned: Bool)->Void)?, failure: FailureClosure?) {
pin(.PinsAdd, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
public func unpinItem(_ channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ unpinned: Bool)->Void)?, failure: FailureClosure?) {
pin(.PinsRemove, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func pin(_ endpoint: Endpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["channel": channel, "file": file, "file_comment": fileComment, "timestamp": timestamp]
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(true)
}){(error) -> Void in
failure?(error)
}
}
//MARK: - Reactions
// One of file, file_comment, or the combination of channel and timestamp must be specified.
public func addReaction(_ name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ reacted: Bool)->Void)?, failure: FailureClosure?) {
react(.ReactionsAdd, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
// One of file, file_comment, or the combination of channel and timestamp must be specified.
public func removeReaction(_ name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ unreacted: Bool)->Void)?, failure: FailureClosure?) {
react(.ReactionsRemove, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func react(_ endpoint: Endpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["name": name, "file": file, "file_comment": fileComment, "channel": channel, "timestamp": timestamp]
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Stars
// One of file, file_comment, channel, or the combination of channel and timestamp must be specified.
public func addStar(_ file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ starred: Bool)->Void)?, failure: FailureClosure?) {
star(.StarsAdd, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
// One of file, file_comment, channel, or the combination of channel and timestamp must be specified.
public func removeStar(_ file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((_ unstarred: Bool)->Void)?, failure: FailureClosure?) {
star(.StarsRemove, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(ok)
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func star(_ endpoint: Endpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((_ ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["file": file, "file_comment": fileComment, "channel": channel, "timestamp": timestamp]
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Team
public func teamInfo(_ success: ((_ info: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(.TeamInfo, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(response["team"] as? [String: Any])
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Users
public func userPresence(_ user: String, success: ((_ presence: String?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user": user]
networkInterface.request(.UsersGetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(response["presence"] as? String)
}){(error) -> Void in
failure?(error)
}
}
public func userInfo(_ id: String, success: ((_ user: User)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user": id]
networkInterface.request(.UsersInfo, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(User(user: response["user"] as? [String: Any]))
}) {(error) -> Void in
failure?(error)
}
}
public func usersList(_ includePresence: Bool = false, success: ((_ userList: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence": includePresence]
networkInterface.request(.UsersList, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(response["members"] as? [[String: Any]])
}){(error) -> Void in
failure?(error)
}
}
public func setUserActive(_ success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
networkInterface.request(.UsersSetActive, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
public func setUserPresence(_ presence: Presence, success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence": presence.rawValue]
networkInterface.request(.UsersSetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Channel Utilities
fileprivate func close(_ endpoint: Endpoint, channelID: String, success: ((_ closed: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channelID]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func history(_ endpoint: Endpoint, id: String, latest: String = "\(Date().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((_ history: History)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": id, "latest": latest, "oldest": oldest, "inclusive": inclusive, "count": count, "unreads": unreads]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(History(history: response))
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func info(_ endpoint: Endpoint, type: ChannelType, id: String, success: ((_ channel: Channel)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": id]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(Channel(channel: response[type.rawValue] as? [String: Any]))
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func list(_ endpoint: Endpoint, type: ChannelType, excludeArchived: Bool = false, success: ((_ channels: [[String: Any]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["exclude_archived": excludeArchived]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(response[type.rawValue+"s"] as? [[String: Any]])
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func mark(_ endpoint: Endpoint, channel: String, timestamp: String, success: ((_ ts: String)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, "ts": timestamp]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(timestamp)
}) {(error) -> Void in
failure?(error)
}
}
fileprivate func setInfo(_ endpoint: Endpoint, type: InfoType, channel: String, text: String, success: ((_ success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, type.rawValue: text]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Encode Attachments
fileprivate func encodeAttachments(_ attachments: [Attachment?]?) -> String? {
if let attachments = attachments {
var attachmentArray: [[String: Any]] = []
for attachment in attachments {
if let attachment = attachment {
attachmentArray.append(attachment.dictionary)
}
}
do {
let data = try JSONSerialization.data(withJSONObject: attachmentArray, options: [])
return String(data: data, encoding: String.Encoding.utf8)
} catch _ {
}
}
return nil
}
//MARK: - Filter Nil Parameters
internal static 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
}
//MARK: - Enumerate Do Not Disturb Status
fileprivate func enumerateDNDStatuses(_ statuses: [String: Any]) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
for key in statuses.keys {
retVal[key] = DoNotDisturbStatus(status: statuses[key] as? [String: Any])
}
return retVal
}
}
-49
View File
@@ -1,49 +0,0 @@
//
// WebhookRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct WebhookRequest: Request {
let token: String?
let teamID: String
let teamDomain: String
let channelID: String
let channelName: String
let userID: String
let userName: String
let command: String
let text: String
let responseURL: String
init(request: [String: Any]?) {
token = request?["token"] as? String
teamID = request?["team_id"] as? String ?? ""
teamDomain = request?["team_domain"] as? String ?? ""
channelID = request?["channel_id"] as? String ?? ""
channelName = request?["channel_name"] as? String ?? ""
userID = request?["user_id"] as? String ?? ""
userName = request?["user_name"] as? String ?? ""
command = request?["command"] as? String ?? ""
text = request?["text"] as? String ?? ""
responseURL = request?["response_url"] as? String ?? ""
}
}
-49
View File
@@ -1,49 +0,0 @@
//
// WebhookServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
open class WebhookServer: Server {
public init(token: String, route: String, response: Response) {
super.init(token: token)
addRoute(route, response: response)
}
open func addRoute(_ route: String, response: Response) {
http["/\(route)"] = { request in
let webhookRequest = WebhookRequest(request: self.dictionaryFromRequest(request.body))
if webhookRequest.token == self.token {
return self.request(webhookRequest, reply: self.replyForResponse(response))
} else {
return .badRequest(.text("Bad request."))
}
}
}
fileprivate func replyForResponse(_ response: Response) -> Reply {
if response.attachments == nil && response.responseType == nil {
return Reply.text(body: response.text)
} else {
return Reply.json(response: response)
}
}
}
-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>