Compare commits

..

43 Commits

Author SHA1 Message Date
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
Peter Zignego b2be8d0170 Merge pull request #24 from pvzig/1.0.1
v1.0.1
2016-04-09 11:40:17 -04:00
Peter Zignego 45be1b7a3f Bump version number 2016-04-09 11:33:22 -04:00
Peter Zignego 09aa72d43e Update readme to include explanation of Leaderboard example 2016-04-09 11:24:47 -04:00
Peter Zignego bf4b55bbd6 Add leaderboard bot example 2016-04-09 11:02:30 -04:00
Peter Zignego a82279fad1 Code cleanup and improvement 2016-04-04 23:49:25 -04:00
Peter Zignego 500e489d5d Add AttachmentField type and AttachmentColor enum 2016-04-04 21:26:09 -04:00
Peter Zignego 874f4f51e1 Fix send message optional parameter handling 2016-04-03 21:04:49 -04:00
Peter Zignego 654f419f4e Support for rtm.start parameters 2016-04-03 15:45:53 -04:00
Peter Zignego a7c25fe33b Version bump 2016-03-22 21:55:26 -04:00
40 changed files with 1596 additions and 2700 deletions
+1
View File
@@ -18,6 +18,7 @@ DerivedData
*.xcuserstate
.build
Packages/
*.xcodeproj/
# CocoaPods
#
+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),
]
)
+167
View File
@@ -0,0 +1,167 @@
//
// main.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 String
import SlackKit
class Leaderboard: MessageEventsDelegate {
var leaderboard: [String: Int] = [String: Int]()
let atSet = CharacterSet(characters: ["@"])
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 messageReceived(message: Message) {
listen(message: message)
}
func messageSent(message: Message){}
func messageChanged(message: Message){}
func messageDeleted(message: Message?){}
// MARK: Leaderboard Internal Logic
private func listen(message: Message) {
if let id = client.authenticatedUser?.id, text = message.text {
if text.lowercased().contains(query: Command.Leaderboard.rawValue) && text.contains(query: id) {
handleCommand(command: .Leaderboard, channel: message.channel)
}
}
if message.text?.contains(query: Trigger.PlusPlus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .PlusPlus)
}
if message.text?.contains(query: Trigger.MinusMinus.rawValue) == true {
handleMessageWithTrigger(message: message, trigger: .MinusMinus)
}
}
private func handleMessageWithTrigger(message: Message, trigger: Trigger) {
if let text = message.text,
start = text.index(of: "@"),
end = text.index(of: trigger.rawValue) {
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
}, 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(isOrderedBefore: ({dictionary[$0] > dictionary[$1]})).filter({dictionary[$0] > 0})
let sortedValues = dictionary.values.sorted(isOrderedBefore: {$0 > $1}).filter({$0 > 0})
return leaderboardString(keys: sortedKeys, values: sortedValues)
}
private func bottomItems( dictionary: inout [String: Int]) -> String {
let sortedKeys = dictionary.keys.sorted(isOrderedBefore: ({dictionary[$0] < dictionary[$1]})).filter({dictionary[$0] < 0})
let sortedValues = dictionary.values.sorted(isOrderedBefore: {$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(query: key) {
returnString.replace(string: key, with: "@"+name)
}
}
}
return returnString
}
private func userID(string: String) -> String {
let alphanumericSet = CharacterSet(characters:
["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"0","1","2","3","4","5","6","7","8","9"])
return string.trim(alphanumericSet.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,139 @@
//
// main.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 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 messageReceived(message: Message) {
if let id = client.authenticatedUser?.id {
if message.text?.contains(query: id) == true {
handleMessage(message: message)
}
}
}
func messageSent(message: Message){}
func messageChanged(message: Message){}
func messageDeleted(message: Message?){}
private func handleMessage(message: Message) {
if let text = message.text?.lowercased(), channel = message.channel {
for (robot, verdict) in verdicts {
let lowerbot = robot.lowercased()
if text.contains(query: lowerbot) {
if verdict == true {
client.webAPI.addReaction(name: "robot_face", timestamp: message.ts, channel: channel, success: nil, failure: nil)
} else {
client.webAPI.addReaction(name: "no_entry_sign", timestamp: message.ts, channel: channel, success: nil, failure: nil)
}
return
}
}
client.webAPI.addReaction(name: "question", timestamp: message.ts, channel: channel, success: nil, failure: nil)
}
}
}
let slackbot = RobotOrNotBot(token: "xoxb-SLACK_API_TOKEN")
slackbot.client.connect()
-40
View File
@@ -1,40 +0,0 @@
//
// AppDelegate.swift
// OSX-Sample
//
// Created by Peter Zignego on 2/18/16.
// Copyright © 2016 Launch Software LLC. All rights reserved.
//
import Cocoa
import SlackKit
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, SlackEventsDelegate {
@IBOutlet weak var window: NSWindow!
let client = Client(apiToken: "")
func applicationDidFinishLaunching(aNotification: NSNotification) {
client.connect()
client.slackEventsDelegate = self
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
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) {}
}
@@ -1,58 +0,0 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
-680
View File
@@ -1,680 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6233" systemVersion="14A329f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6233"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OSX-Sample" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OSX-Sample" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About OSX-Sample" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide OSX-Sample" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit OSX-Sample" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="OSX-Sample Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="OSX-Sample" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>
-34
View File
@@ -1,34 +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>CFBundleIconFile</key>
<string></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>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Launch Software LLC. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
+5 -3
View File
@@ -27,7 +27,9 @@ let package = Package(
name: "SlackKit",
targets: [],
dependencies: [
.Package(url: "https://github.com/pvzig/Starscream.git",
majorVersion: 1),
]
.Package(url: "https://github.com/open-swift/C7.git", majorVersion: 0, minor: 7),
.Package(url: "https://github.com/czechboy0/Jay.git", majorVersion: 0, minor: 6),
.Package(url: "https://github.com/Zewo/WebSocket", majorVersion: 0, minor: 6),
],
exclude: ["Examples"]
)
-7
View File
@@ -1,7 +0,0 @@
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'SlackKit' do
pod 'Starscream'
end
-10
View File
@@ -1,10 +0,0 @@
PODS:
- Starscream (1.1.2)
DEPENDENCIES:
- Starscream
SPEC CHECKSUMS:
Starscream: 58a12fd35a3cb6aaa105716c2d42765f7c1c732f
COCOAPODS: 0.39.0
+23 -17
View File
@@ -1,9 +1,12 @@
![SlackKit](https://cloud.githubusercontent.com/assets/8311605/10260893/5ec60f96-694e-11e5-91fd-da6845942201.png)
##iOS/OS X Slack Client Library
##Alpha Linux Slack Client Library
###Description
This is a Slack client library for iOS and OS X 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 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).
###Disclaimer: The linux version of SlackKit is a pre-release alpha. Feel free to report issues you come across.
###Installation
####Swift Package Manager
Add SlackKit to your Package.swift
@@ -12,35 +15,43 @@ import PackageDescription
let package = Package(
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0)
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0, minor: 0)
]
)
```
Run `swift-build` on your applications main directory.
####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-03-a
swiftenv local DEVELOPMENT-SNAPSHOT-2016-05-03-a
```
5. Install and Link OpenSSL: `brew install openssl`, `brew link openssl --force`
####CocoaPods
Add the pod to your podfile:
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:
```
pod 'SlackKit'
```
and run
```
pod install
swift build
swift build -Xlinker -L$(pwd)/.build/debug/ -Xswiftc -I/usr/local/include -Xlinker -L/usr/local/lib -X
```
To use the library in your project import it:
```
import SlackKit
```
####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).
###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).
Once you have a token, initialize a client instance using it:
```swift
let client = Client(apiToken: "YOUR_SLACK_API_TOKEN")
```
If you want to receive messages from the Slack RTM API, connect to it.
@@ -48,11 +59,6 @@ If you want to receive messages from the Slack RTM API, connect to it.
client.connect()
```
You can also set options for a ping/pong interval, timeout interval, and automatic reconnection:
```swift
client.connect(pingInterval: 2, timeout: 10, reconnect: false)
```
Once connected, the client will begin to consume any messages sent by the Slack RTM API.
####Web API Methods
-30
View File
@@ -1,30 +0,0 @@
//
// SlackKit.h
//
// 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/Foundation.h>
//! Project version number for SlackKit.
FOUNDATION_EXPORT double SlackKitVersionNumber;
//! Project version string for SlackKit.
FOUNDATION_EXPORT const unsigned char SlackKitVersionString[];
-17
View File
@@ -1,17 +0,0 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "0.9.9"
s.summary = "a Slack client library for iOS and OS X written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
s.author = { "Peter Zignego" => "peter@launchsoft.co" }
s.source = { :git => "https://github.com/pvzig/SlackKit.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/pvzig'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.requires_arc = true
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.1.2'
end
-531
View File
@@ -1,531 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
2601D6271C7688610012BF22 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D6261C7688610012BF22 /* AppDelegate.swift */; };
2601D6291C7688610012BF22 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2601D6281C7688610012BF22 /* Assets.xcassets */; };
2601D62C1C7688610012BF22 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2601D62A1C7688610012BF22 /* MainMenu.xib */; };
260EC2331C4DC61D0093B253 /* ClientExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* ClientExtensions.swift */; };
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.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 */; };
26BBA1991C398E3C00BF7225 /* EventDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */; };
26BBA19A1C398E3C00BF7225 /* EventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18D1C398E3C00BF7225 /* EventHandler.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 */; };
26BBA19E1C398E3C00BF7225 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.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 */; };
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPIErrorDispatcher.swift; path = Sources/SlackWebAPIErrorDispatcher.swift; sourceTree = "<group>"; };
2601D6241C7688610012BF22 /* OSX-Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "OSX-Sample.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2601D6261C7688610012BF22 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
2601D6281C7688610012BF22 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
2601D62B1C7688610012BF22 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
2601D62D1C7688610012BF22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
26072A341BB48B3A00CD650C /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
260EC2301C4DC61D0093B253 /* ClientExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClientExtensions.swift; path = Sources/ClientExtensions.swift; sourceTree = "<group>"; };
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkInterface.swift; path = Sources/NetworkInterface.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPI.swift; path = Sources/SlackWebAPI.swift; sourceTree = "<group>"; };
2661A6A41BBF62FF0026F67B /* SlackKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SlackKit.h; sourceTree = "<group>"; };
266E05F01BBF780C00840D76 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; 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>"; };
26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventDispatcher.swift; path = Sources/EventDispatcher.swift; sourceTree = "<group>"; };
26BBA18D1C398E3C00BF7225 /* EventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventHandler.swift; path = Sources/EventHandler.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>"; };
26BBA1911C398E3C00BF7225 /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Types.swift; path = Sources/Types.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>"; };
407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SlackKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit.release.xcconfig"; sourceTree = "<group>"; };
F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SlackKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2601D6211C7688610012BF22 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A301BB48B3A00CD650C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0C2928E508A686B69F9F0117 /* Pods */ = {
isa = PBXGroup;
children = (
F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */,
4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
2601D6251C7688610012BF22 /* OSX-Sample */ = {
isa = PBXGroup;
children = (
2601D6261C7688610012BF22 /* AppDelegate.swift */,
2601D6281C7688610012BF22 /* Assets.xcassets */,
2601D62A1C7688610012BF22 /* MainMenu.xib */,
2601D62D1C7688610012BF22 /* Info.plist */,
);
path = "OSX-Sample";
sourceTree = "<group>";
};
26072A2A1BB48B3A00CD650C = {
isa = PBXGroup;
children = (
2661A6811BBF60E60026F67B /* SlackKit */,
2601D6251C7688610012BF22 /* OSX-Sample */,
26072A351BB48B3A00CD650C /* Products */,
0C2928E508A686B69F9F0117 /* Pods */,
CA70A3A1A9A1A259960DFBCF /* Frameworks */,
);
sourceTree = "<group>";
};
26072A351BB48B3A00CD650C /* Products */ = {
isa = PBXGroup;
children = (
26072A341BB48B3A00CD650C /* SlackKit.framework */,
2601D6241C7688610012BF22 /* OSX-Sample.app */,
);
name = Products;
sourceTree = "<group>";
};
2661A6811BBF60E60026F67B /* SlackKit */ = {
isa = PBXGroup;
children = (
26DF40341C7A0FA300E19241 /* Attachment.swift */,
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
26BBA1891C398E3C00BF7225 /* Client.swift */,
260EC2301C4DC61D0093B253 /* ClientExtensions.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */,
26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */,
26BBA18D1C398E3C00BF7225 /* EventHandler.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */,
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */,
2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */,
26BBA1901C398E3C00BF7225 /* Team.swift */,
26BBA1911C398E3C00BF7225 /* Types.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
266E05F01BBF780C00840D76 /* Info.plist */,
);
path = SlackKit;
sourceTree = "<group>";
};
CA70A3A1A9A1A259960DFBCF /* Frameworks */ = {
isa = PBXGroup;
children = (
407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
26072A311BB48B3A00CD650C /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
2601D6231C7688610012BF22 /* OSX-Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2601D62E1C7688610012BF22 /* Build configuration list for PBXNativeTarget "OSX-Sample" */;
buildPhases = (
2601D6201C7688610012BF22 /* Sources */,
2601D6211C7688610012BF22 /* Frameworks */,
2601D6221C7688610012BF22 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "OSX-Sample";
productName = "OSX-Sample";
productReference = 2601D6241C7688610012BF22 /* OSX-Sample.app */;
productType = "com.apple.product-type.application";
};
26072A331BB48B3A00CD650C /* SlackKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = 26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit" */;
buildPhases = (
EBD7A091EB278C5BA34791C5 /* Check Pods Manifest.lock */,
26072A2F1BB48B3A00CD650C /* Sources */,
26072A301BB48B3A00CD650C /* Frameworks */,
26072A311BB48B3A00CD650C /* Headers */,
26072A321BB48B3A00CD650C /* Resources */,
AC19A945F408269E4B4132CC /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = SlackKit;
productName = SlackRTMKit;
productReference = 26072A341BB48B3A00CD650C /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
26072A2B1BB48B3A00CD650C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "Launch Software LLC";
TargetAttributes = {
2601D6231C7688610012BF22 = {
CreatedOnToolsVersion = 7.2.1;
};
26072A331BB48B3A00CD650C = {
CreatedOnToolsVersion = 7.0;
};
};
};
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 */,
2601D6231C7688610012BF22 /* OSX-Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
2601D6221C7688610012BF22 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2601D6291C7688610012BF22 /* Assets.xcassets in Resources */,
2601D62C1C7688610012BF22 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A321BB48B3A00CD650C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
AC19A945F408269E4B4132CC /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit-resources.sh\"\n";
showEnvVarsInLog = 0;
};
EBD7A091EB278C5BA34791C5 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2601D6201C7688610012BF22 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2601D6271C7688610012BF22 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A2F1BB48B3A00CD650C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26BBA1991C398E3C00BF7225 /* EventDispatcher.swift in Sources */,
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */,
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */,
26BBA19E1C398E3C00BF7225 /* Types.swift in Sources */,
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */,
26BBA19A1C398E3C00BF7225 /* EventHandler.swift in Sources */,
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */,
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */,
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */,
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */,
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */,
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */,
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */,
260EC2331C4DC61D0093B253 /* ClientExtensions.swift in Sources */,
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */,
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */,
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
2601D62A1C7688610012BF22 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
2601D62B1C7688610012BF22 /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
2601D62F1C7688610012BF22 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "OSX-Sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "LS.OSX-Sample";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
2601D6301C7688610012BF22 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "OSX-Sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "LS.OSX-Sample";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
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;
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;
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;
baseConfigurationReference = F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */;
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_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
26072A3E1BB48B3B00CD650C /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */;
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_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2601D62E1C7688610012BF22 /* Build configuration list for PBXNativeTarget "OSX-Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2601D62F1C7688610012BF22 /* Debug */,
2601D6301C7688610012BF22 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
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" */ = {
isa = XCConfigurationList;
buildConfigurations = (
26072A3D1BB48B3B00CD650C /* Debug */,
26072A3E1BB48B3B00CD650C /* 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>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:SlackKit.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.9</string>
<string>1.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
-30
View File
@@ -1,30 +0,0 @@
//
// SlackKit.h
//
// 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/Foundation.h>
//! Project version number for SlackKit.
FOUNDATION_EXPORT double SlackKitVersionNumber;
//! Project version string for SlackKit.
FOUNDATION_EXPORT const unsigned char SlackKitVersionString[];
+53 -9
View File
@@ -21,8 +21,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct Attachment {
public let fallback: String?
@@ -34,11 +32,11 @@ public struct Attachment {
public let title: String?
public let titleLink: String?
public let text: String?
public let fields: [[String: AnyObject]]?
public let fields: [AttachmentField]?
public let imageURL: String?
public let thumbURL: String?
internal init?(attachment: [String: AnyObject]?) {
internal init?(attachment: [String: Any]?) {
fallback = attachment?["fallback"] as? String
color = attachment?["color"] as? String
pretext = attachment?["pretext"] as? String
@@ -48,12 +46,14 @@ public struct Attachment {
title = attachment?["title"] as? String
titleLink = attachment?["title_link"] as? String
text = attachment?["text"] as? String
fields = attachment?["fields"] as? [[String: AnyObject]]
imageURL = attachment?["image_url"] as? String
thumbURL = attachment?["thumb_url"] as? String
fields = (attachment?["fields"] as? [Any])?.objectArrayFromDictionaryArray(intializer: {(field) -> AttachmentField? in
return AttachmentField(field: field)
})
}
public init?(fallback: String, title:String, colorHex: String? = nil, pretext: String? = nil, authorName: String? = nil, authorLink: String? = nil, authorIcon: String? = nil, titleLink: String? = nil, text: String? = nil, fields: [[String: AnyObject]]? = nil, imageURL: String? = nil, thumbURL: String? = nil) {
public init?(fallback: String, title:String, colorHex: String? = nil, pretext: String? = nil, authorName: String? = nil, authorLink: String? = nil, authorIcon: String? = nil, titleLink: String? = nil, text: String? = nil, fields: [AttachmentField]? = nil, imageURL: String? = nil, thumbURL: String? = nil) {
self.fallback = fallback
self.color = colorHex
self.pretext = pretext
@@ -68,8 +68,8 @@ public struct Attachment {
self.thumbURL = thumbURL
}
internal func dictionary() -> [String: AnyObject] {
var attachment = [String: AnyObject]()
internal func dictionary() -> [String: Any] {
var attachment = [String: Any]()
attachment["fallback"] = fallback
attachment["color"] = color
attachment["pretext"] = pretext
@@ -79,10 +79,54 @@ public struct Attachment {
attachment["title"] = title
attachment["title_link"] = titleLink
attachment["text"] = text
attachment["fields"] = fields
attachment["fields"] = fieldJSONArray(fields: fields)
attachment["image_url"] = imageURL
attachment["thumb_url"] = thumbURL
return attachment
}
private func fieldJSONArray(fields: [AttachmentField]?) -> [Any] {
var returnValue = [Any]()
if let f = fields {
for field in f {
returnValue.append(field.dictionary())
}
}
return returnValue
}
}
public struct AttachmentField {
public let title: String?
public let value: String?
public let short: Bool?
internal init?(field: [String: Any]?) {
title = field?["title"] as? String
value = field?["value"] as? String
short = field?["short"] as? Bool
}
public init(title:String, value:String, short: Bool? = nil) {
self.title = title
self.value = value.slackFormatEscaping()
self.short = short
}
internal func dictionary() -> [String: Any] {
var field = [String: Any]()
field["title"] = title
field["value"] = value
field["short"] = short
return field
}
}
public enum AttachmentColor: String {
case Good = "good"
case Warning = "warning"
case Danger = "danger"
}
+3 -3
View File
@@ -25,12 +25,12 @@ public struct Bot {
public let id: String?
internal(set) public var name: String?
internal(set) public var icons: [String: AnyObject]?
internal(set) public var icons: [String: Any]?
internal init?(bot: [String: AnyObject]?) {
internal init?(bot: [String: Any]?) {
id = bot?["id"] as? String
name = bot?["name"] as? String
icons = bot?["icons"] as? [String: AnyObject]
icons = bot?["icons"] as? [String: Any]
}
}
+9 -4
View File
@@ -49,7 +49,7 @@ public struct Channel {
internal(set) public var usersTyping = [String]()
internal(set) public var messages = [String: Message]()
internal init?(channel: [String: AnyObject]?) {
internal init?(channel: [String: Any]?) {
id = channel?["id"] as? String
name = channel?["name"] as? String
created = channel?["created"] as? Int
@@ -62,15 +62,20 @@ public struct Channel {
isUserDeleted = channel?["is_user_deleted"] as? Bool
user = channel?["user"] as? String
isOpen = channel?["is_open"] as? Bool
topic = Topic(topic: channel?["topic"] as? [String: AnyObject])
purpose = Topic(topic: channel?["purpose"] as? [String: AnyObject])
topic = Topic(topic: channel?["topic"] as? [String: Any])
purpose = Topic(topic: channel?["purpose"] as? [String: Any])
isMember = channel?["is_member"] as? Bool
lastRead = channel?["last_read"] as? String
latest = Message(message: channel?["latest"] as? [String: AnyObject])
unread = channel?["unread_count"] as? Int
unreadCountDisplay = channel?["unread_count_display"] as? Int
hasPins = channel?["has_pins"] as? Bool
members = channel?["members"] as? [String]
if (Message(message: channel?["latest"] as? [String: Any])?.ts == nil) {
latest = Message(ts: channel?["latest"] as? String)
} else {
latest = Message(message: channel?["latest"] as? [String: Any])
}
}
internal init?(id:String?) {
@@ -1,5 +1,5 @@
//
// EventDispatcher.swift
// Client+EventDispatching.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -21,143 +21,137 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal class EventDispatcher {
let client: Client
let handler: EventHandler
internal extension SlackClient {
required init(client: Client) {
self.client = client
handler = EventHandler(client: client)
}
func dispatch(event: [String: AnyObject]) {
func dispatch(event: [String: Any]) {
let event = Event(event: event)
if let type = event.type {
switch type {
case .Hello:
handler.connected()
connected = true
slackEventsDelegate?.clientConnected()
case .Ok:
handler.messageSent(event)
messageSent(event: event)
case .Message:
if (event.subtype != nil) {
messageDispatcher(event)
messageDispatcher(event: event)
} else {
handler.messageReceived(event)
messageReceived(event: event)
}
case .UserTyping:
handler.userTyping(event)
userTyping(event: event)
case .ChannelMarked, .IMMarked, .GroupMarked:
handler.channelMarked(event)
channelMarked(event: event)
case .ChannelCreated, .IMCreated:
handler.channelCreated(event)
channelCreated(event: event)
case .ChannelJoined, .GroupJoined:
handler.channelJoined(event)
channelJoined(event: event)
case .ChannelLeft, .GroupLeft:
handler.channelLeft(event)
channelLeft(event: event)
case .ChannelDeleted:
handler.channelDeleted(event)
channelDeleted(event: event)
case .ChannelRenamed, .GroupRename:
handler.channelRenamed(event)
channelRenamed(event: event)
case .ChannelArchive, .GroupArchive:
handler.channelArchived(event, archived: true)
channelArchived(event: event, archived: true)
case .ChannelUnarchive, .GroupUnarchive:
handler.channelArchived(event, archived: false)
channelArchived(event: event, archived: false)
case .ChannelHistoryChanged, .IMHistoryChanged, .GroupHistoryChanged:
handler.channelHistoryChanged(event)
channelHistoryChanged(event: event)
case .DNDUpdated:
handler.doNotDisturbUpdated(event)
doNotDisturbUpdated(event: event)
case .DNDUpatedUser:
handler.doNotDisturbUserUpdated(event)
doNotDisturbUserUpdated(event: event)
case .IMOpen, .GroupOpen:
handler.open(event, open: true)
open(event: event, open: true)
case .IMClose, .GroupClose:
handler.open(event, open: false)
open(event: event, open: false)
case .FileCreated:
handler.processFile(event)
processFile(event: event)
case .FileShared:
handler.processFile(event)
processFile(event: event)
case .FileUnshared:
handler.processFile(event)
processFile(event: event)
case .FilePublic:
handler.processFile(event)
processFile(event: event)
case .FilePrivate:
handler.filePrivate(event)
filePrivate(event: event)
case .FileChanged:
handler.processFile(event)
processFile(event: event)
case .FileDeleted:
handler.deleteFile(event)
deleteFile(event: event)
case .FileCommentAdded:
handler.fileCommentAdded(event)
fileCommentAdded(event: event)
case .FileCommentEdited:
handler.fileCommentEdited(event)
fileCommentEdited(event: event)
case .FileCommentDeleted:
handler.fileCommentDeleted(event)
fileCommentDeleted(event: event)
case .PinAdded:
handler.pinAdded(event)
pinAdded(event: event)
case .PinRemoved:
handler.pinRemoved(event)
pinRemoved(event: event)
case .Pong:
handler.pong(event)
pong(event: event)
case .PresenceChange:
handler.presenceChange(event)
presenceChange(event: event)
case .ManualPresenceChange:
handler.manualPresenceChange(event)
manualPresenceChange(event: event)
case .PrefChange:
handler.changePreference(event)
changePreference(event: event)
case .UserChange:
handler.userChange(event)
userChange(event: event)
case .TeamJoin:
handler.teamJoin(event)
teamJoin(event: event)
case .StarAdded:
handler.itemStarred(event, star: true)
itemStarred(event: event, star: true)
case .StarRemoved:
handler.itemStarred(event, star: false)
itemStarred(event: event, star: false)
case .ReactionAdded:
handler.addedReaction(event)
addedReaction(event: event)
case .ReactionRemoved:
handler.removedReaction(event)
removedReaction(event: event)
case .EmojiChanged:
handler.emojiChanged(event)
emojiChanged(event: event)
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:
handler.teamPlanChange(event)
teamPlanChange(event: event)
case .TeamPrefChange:
handler.teamPreferenceChange(event)
teamPreferenceChange(event: event)
case .TeamRename:
handler.teamNameChange(event)
teamNameChange(event: event)
case .TeamDomainChange:
handler.teamDomainChange(event)
teamDomainChange(event: event)
case .EmailDomainChange:
handler.emailDomainChange(event)
emailDomainChange(event: event)
case .TeamProfileChange:
handler.teamProfileChange(event)
teamProfileChange(event: event)
case .TeamProfileDelete:
handler.teamProfileDeleted(event)
teamProfileDeleted(event: event)
case .TeamProfileReorder:
handler.teamProfileReordered(event)
teamProfileReordered(event: event)
case .BotAdded:
handler.bot(event)
bot(event: event)
case .BotChanged:
handler.bot(event)
bot(event: event)
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:
client.connect(pingInterval: client.pingInterval, timeout: client.timeout, reconnect: client.reconnect)
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
case .ReconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
case .SubteamCreated, .SubteamUpdated:
handler.subteam(event)
subteam(event: event)
case .SubteamSelfAdded:
handler.subteamAddedSelf(event)
subteamAddedSelf(event: event)
case.SubteamSelfRemoved:
handler.subteamRemovedSelf(event)
subteamRemovedSelf(event: event)
case .Error:
print("Error: \(event)")
break
@@ -169,11 +163,11 @@ internal class EventDispatcher {
let subtype = MessageSubtype(rawValue: event.subtype!)!
switch subtype {
case .MessageChanged:
handler.messageChanged(event)
messageChanged(event: event)
case .MessageDeleted:
handler.messageDeleted(event)
messageDeleted(event: event)
default:
handler.messageReceived(event)
messageReceived(event: event)
}
}
+547
View File
@@ -0,0 +1,547 @@
//
// Client+EventHandling.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 Venice
internal extension SlackClient {
//MARK: - Pong
func pong(event: Event) {
pong = event.replyTo
}
//MARK: - Messages
func messageSent(event: Event) {
if let reply = event.replyTo, message = sentMessages["\(reply)"], channel = message.channel, ts = message.ts {
message.ts = event.ts
message.text = event.text
channels[channel]?.messages[ts] = message
messageEventsDelegate?.messageSent(message: message)
}
}
func messageReceived(event: Event) {
if let channel = event.channel, message = event.message, id = channel.id, ts = message.ts {
channels[id]?.messages[ts] = message
messageEventsDelegate?.messageReceived(message: message)
}
}
func messageChanged(event: Event) {
if let id = event.channel?.id, nested = event.nestedMessage, ts = nested.ts {
channels[id]?.messages[ts] = nested
messageEventsDelegate?.messageChanged(message: nested)
}
}
func messageDeleted(event: Event) {
if let id = event.channel?.id, key = event.message?.deletedTs {
let message = channels[id]?.messages[key]
channels[id]?.messages.removeValue(forKey:key)
messageEventsDelegate?.messageDeleted(message: message)
}
}
//MARK: - Channels
func userTyping(event: Event) {
if let channelID = event.channel?.id, userID = event.user?.id {
if let _ = channels[channelID] {
if (!channels[channelID]!.usersTyping.contains(userID)) {
channels[channelID]?.usersTyping.append(userID)
channelEventsDelegate?.userTyping(channel: event.channel, user: event.user)
}
}
co { [weak self] in
let weakSelf = self
nap(for: 5.0.seconds)
if let index = weakSelf?.channels[channelID]?.usersTyping.index(of:userID) {
weakSelf?.channels[channelID]?.usersTyping.remove(at: index)
}
}
}
}
func channelMarked(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id]?.lastRead = event.ts
channelEventsDelegate?.channelMarked(channel: channel, timestamp: event.ts)
}
//TODO: Recalculate unreads
}
func channelCreated(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id] = channel
channelEventsDelegate?.channelCreated(channel: channel)
}
}
func channelDeleted(event: Event) {
if let channel = event.channel, id = channel.id {
channels.removeValue(forKey:id)
channelEventsDelegate?.channelDeleted(channel: channel)
}
}
func channelJoined(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id] = event.channel
channelEventsDelegate?.channelJoined(channel: channel)
}
}
func channelLeft(event: Event) {
if let channel = event.channel, id = channel.id, userID = authenticatedUser?.id {
if let index = channels[id]?.members?.index(of:userID) {
channels[id]?.members?.remove(at: index)
channelEventsDelegate?.channelLeft(channel: channel)
}
}
}
func channelRenamed(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id]?.name = channel.name
channelEventsDelegate?.channelRenamed(channel: channel)
}
}
func channelArchived(event: Event, archived: Bool) {
if let channel = event.channel, id = channel.id {
channels[id]?.isArchived = archived
channelEventsDelegate?.channelArchived(channel: channel)
}
}
func channelHistoryChanged(event: Event) {
if let channel = event.channel {
//TODO: Reload chat history if there are any cached messages before latest
channelEventsDelegate?.channelHistoryChanged(channel: channel)
}
}
//MARK: - Do Not Disturb
func doNotDisturbUpdated(event: Event) {
if let dndStatus = event.dndStatus {
authenticatedUser?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUpdated(dndStatus: dndStatus)
}
}
func doNotDisturbUserUpdated(event: Event) {
if let dndStatus = event.dndStatus, user = event.user, id = user.id {
users[id]?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUserUpdated(dndStatus: dndStatus, user: user)
}
}
//MARK: - IM & Group Open/Close
func open(event: Event, open: Bool) {
if let channel = event.channel, id = channel.id {
channels[id]?.isOpen = open
groupEventsDelegate?.groupOpened(group: channel)
}
}
//MARK: - Files
func processFile(event: Event) {
if let file = event.file, id = file.id {
if let comment = file.initialComment, commentID = comment.id {
if files[id]?.comments[commentID] == nil {
files[id]?.comments[commentID] = comment
}
}
files[id] = file
fileEventsDelegate?.fileProcessed(file: file)
}
}
func filePrivate(event: Event) {
if let file = event.file, id = file.id {
files[id]?.isPublic = false
fileEventsDelegate?.fileMadePrivate(file: file)
}
}
func deleteFile(event: Event) {
if let file = event.file, id = file.id {
if files[id] != nil {
files.removeValue(forKey:id)
}
fileEventsDelegate?.fileDeleted(file: file)
}
}
func fileCommentAdded(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
files[id]?.comments[commentID] = comment
fileEventsDelegate?.fileCommentAdded(file: file, comment: comment)
}
}
func fileCommentEdited(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
files[id]?.comments[commentID]?.comment = comment.comment
fileEventsDelegate?.fileCommentEdited(file: file, comment: comment)
}
}
func fileCommentDeleted(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
files[id]?.comments.removeValue(forKey:commentID)
fileEventsDelegate?.fileCommentDeleted(file: file, comment: comment)
}
}
//MARK: - Pins
func pinAdded(event: Event) {
if let id = event.channelID, item = event.item {
channels[id]?.pinnedItems.append(item)
pinEventsDelegate?.itemPinned(item: item, channel: channels[id])
}
}
func pinRemoved(event: Event) {
if let id = event.channelID {
if let pins = channels[id]?.pinnedItems.filter({$0 != event.item}) {
channels[id]?.pinnedItems = pins
}
pinEventsDelegate?.itemUnpinned(item: event.item, channel: channels[id])
}
}
//MARK: - Stars
func itemStarred(event: Event, star: Bool) {
if let item = event.item, type = item.type {
switch type {
case "message":
starMessage(item: item, star: star)
case "file":
starFile(item: item, star: star)
case "file_comment":
starComment(item: item)
default:
break
}
starEventsDelegate?.itemStarred(item: item, star: star)
}
}
func starMessage(item: Item, star: Bool) {
if let message = item.message, ts = message.ts, channel = item.channel {
if let _ = channels[channel]?.messages[ts] {
channels[channel]?.messages[ts]?.isStarred = star
}
}
}
func starFile(item: Item, star: Bool) {
if let file = item.file, id = file.id {
files[id]?.isStarred = star
if let stars = files[id]?.stars {
if star == true {
files[id]?.stars = stars + 1
} else {
if stars > 0 {
files[id]?.stars = stars - 1
}
}
}
}
}
func starComment(item: Item) {
if let file = item.file, id = file.id, comment = item.comment, commentID = comment.id {
files[id]?.comments[commentID] = comment
}
}
//MARK: - Reactions
func addedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = channels[channel]?.messages[ts] {
if (message.reactions[key]) == nil {
message.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
message.reactions[key]?.users[userID] = userID
}
}
}
case "file":
if let id = item.file?.id, file = files[id] {
if file.reactions[key] == nil {
files[id]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
files[id]?.reactions[key]?.users[userID] = userID
}
}
case "file_comment":
if let id = item.file?.id, file = files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] == nil {
files[id]?.comments[commentID]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
files[id]?.comments[commentID]?.reactions[key]?.users[userID] = userID
}
}
break
default:
break
}
reactionEventsDelegate?.reactionAdded(reaction: event.reaction, item: event.item, itemUser: event.itemUser)
}
}
func removedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = channels[channel]?.messages[ts] {
if (message.reactions[key]) != nil {
message.reactions[key]?.users.removeValue(forKey:userID)
}
if (message.reactions[key]?.users.count == 0) {
message.reactions.removeValue(forKey:key)
}
}
}
case "file":
if let itemFile = item.file, id = itemFile.id, file = files[id] {
if file.reactions[key] != nil {
files[id]?.reactions[key]?.users.removeValue(forKey:userID)
}
if files[id]?.reactions[key]?.users.count == 0 {
files[id]?.reactions.removeValue(forKey:key)
}
}
case "file_comment":
if let id = item.file?.id, file = files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] != nil {
files[id]?.comments[commentID]?.reactions[key]?.users.removeValue(forKey:userID)
}
if files[id]?.comments[commentID]?.reactions[key]?.users.count == 0 {
files[id]?.comments[commentID]?.reactions.removeValue(forKey:key)
}
}
break
default:
break
}
reactionEventsDelegate?.reactionRemoved(reaction: event.reaction, item: event.item, itemUser: event.itemUser)
}
}
//MARK: - Preferences
func changePreference(event: Event) {
if let name = event.name {
authenticatedUser?.preferences?[name] = event.value
slackEventsDelegate?.preferenceChanged(preference: name, value: event.value)
}
}
//Mark: - User Change
func userChange(event: Event) {
if let user = event.user, id = user.id {
let preferences = users[id]?.preferences
users[id] = user
users[id]?.preferences = preferences
slackEventsDelegate?.userChanged(user: user)
}
}
//MARK: - User Presence
func presenceChange(event: Event) {
if let user = event.user, id = user.id {
users[id]?.presence = event.presence
slackEventsDelegate?.presenceChanged(user: user, presence: event.presence)
}
}
//MARK: - Team
func teamJoin(event: Event) {
if let user = event.user, id = user.id {
users[id] = user
teamEventsDelegate?.teamJoined(user: user)
}
}
func teamPlanChange(event: Event) {
if let plan = event.plan {
team?.plan = plan
teamEventsDelegate?.teamPlanChanged(plan: plan)
}
}
func teamPreferenceChange(event: Event) {
if let name = event.name {
team?.prefs?[name] = event.value
teamEventsDelegate?.teamPreferencesChanged(preference: name, value: event.value)
}
}
func teamNameChange(event: Event) {
if let name = event.name {
team?.name = name
teamEventsDelegate?.teamNameChanged(name: name)
}
}
func teamDomainChange(event: Event) {
if let domain = event.domain {
team?.domain = domain
teamEventsDelegate?.teamDomainChanged(domain: domain)
}
}
func emailDomainChange(event: Event) {
if let domain = event.emailDomain {
team?.emailDomain = domain
teamEventsDelegate?.teamEmailDomainChanged(domain: domain)
}
}
func emojiChanged(event: Event) {
//TODO: Call emoji.list here
teamEventsDelegate?.teamEmojiChanged()
}
//MARK: - Bots
func bot(event: Event) {
if let bot = event.bot, id = bot.id {
bots[id] = bot
slackEventsDelegate?.botEvent(bot: bot)
}
}
//MARK: - Subteams
func subteam(event: Event) {
if let subteam = event.subteam, id = subteam.id {
userGroups[id] = subteam
subteamEventsDelegate?.subteamEvent(userGroup: subteam)
}
}
func subteamAddedSelf(event: Event) {
if let subteamID = event.subteamID, _ = authenticatedUser?.userGroups {
authenticatedUser?.userGroups![subteamID] = subteamID
subteamEventsDelegate?.subteamSelfAdded(subteamID: subteamID)
}
}
func subteamRemovedSelf(event: Event) {
if let subteamID = event.subteamID {
authenticatedUser?.userGroups?.removeValue(forKey:subteamID)
subteamEventsDelegate?.subteamSelfRemoved(subteamID: subteamID)
}
}
//MARK: - Team Profiles
func teamProfileChange(event: Event) {
for user in users {
if let fields = event.profile?.fields {
for key in fields.keys {
users[user.0]?.profile?.customProfile?.fields[key]?.updateProfileField(profile: fields[key])
}
}
}
teamProfileEventsDelegate?.teamProfileChanged(profile: event.profile)
}
func teamProfileDeleted(event: Event) {
for user in users {
if let id = event.profile?.fields.first?.0 {
users[user.0]?.profile?.customProfile?.fields[id] = nil
}
}
teamProfileEventsDelegate?.teamProfileDeleted(profile: event.profile)
}
func teamProfileReordered(event: Event) {
for user in users {
if let keys = event.profile?.fields.keys {
for key in keys {
users[user.0]?.profile?.customProfile?.fields[key]?.ordering = event.profile?.fields[key]?.ordering
}
}
}
teamProfileEventsDelegate?.teamProfileReordered(profile: event.profile)
}
//MARK: - Authenticated User
func manualPresenceChange(event: Event) {
authenticatedUser?.presence = event.presence
slackEventsDelegate?.manualPresenceChanged(user: authenticatedUser, presence: event.presence)
}
}
@@ -1,5 +1,5 @@
//
// ClientExtensions.swift
// Client+Utilities.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -21,17 +21,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
extension Client {
extension SlackClient {
//MARK: - User & Channel
public func getChannelIDByName(name: String) -> String? {
return channels.filter{$0.1.name == stripString(name)}.first?.0
return channels.filter{$0.1.name == stripString(string: name)}.first?.0
}
public func getUserIDByName(name: String) -> String? {
return users.filter{$0.1.name == stripString(name)}.first?.0
return users.filter{$0.1.name == stripString(string: name)}.first?.0
}
public func getImIDForUserWithID(id: String, success: (imID: String?)->Void, failure: (error: SlackError)->Void) {
@@ -40,38 +38,16 @@ extension Client {
if let channel = channel {
success(imID: channel.0)
} else {
webAPI.openIM(id, success: success, failure: failure)
webAPI.openIM(userID: id, success: success, failure: failure)
}
}
//MARK: - Utilities
internal func stripString(string: String) -> String? {
var strippedString: String?
if string[string.startIndex] == "@" {
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
} else if string[string.startIndex] == "#" {
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
var strippedString = string
if string[string.startIndex] == "@" || string[string.startIndex] == "#" {
strippedString.characters.remove(at: string.startIndex)
}
return strippedString
}
}
internal extension String {
func slackFormatEscaping() -> String {
var escapedString = stringByReplacingOccurrencesOfString("&", withString: "&amp;")
escapedString = stringByReplacingOccurrencesOfString("<", withString: "&lt;")
escapedString = stringByReplacingOccurrencesOfString(">", withString: "&gt;")
return escapedString
}
}
public extension NSDate {
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}
}
+132 -149
View File
@@ -21,10 +21,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Starscream
import C7
import Jay
import WebSocket
public class Client: WebSocketDelegate {
public class SlackClient {
internal(set) public var connected = false
internal(set) public var authenticated = false
@@ -39,18 +40,18 @@ public class Client: WebSocketDelegate {
internal(set) public var sentMessages = [String: Message]()
//MARK: - Delegates
public var slackEventsDelegate: SlackEventsDelegate?
public var messageEventsDelegate: MessageEventsDelegate?
public var doNotDisturbEventsDelegate: DoNotDisturbEventsDelegate?
public var channelEventsDelegate: ChannelEventsDelegate?
public var groupEventsDelegate: GroupEventsDelegate?
public var fileEventsDelegate: FileEventsDelegate?
public var pinEventsDelegate: PinEventsDelegate?
public var starEventsDelegate: StarEventsDelegate?
public var reactionEventsDelegate: ReactionEventsDelegate?
public var teamEventsDelegate: TeamEventsDelegate?
public var subteamEventsDelegate: SubteamEventsDelegate?
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?
public weak var slackEventsDelegate: SlackEventsDelegate?
public weak var messageEventsDelegate: MessageEventsDelegate?
public weak var doNotDisturbEventsDelegate: DoNotDisturbEventsDelegate?
public weak var channelEventsDelegate: ChannelEventsDelegate?
public weak var groupEventsDelegate: GroupEventsDelegate?
public weak var fileEventsDelegate: FileEventsDelegate?
public weak var pinEventsDelegate: PinEventsDelegate?
public weak var starEventsDelegate: StarEventsDelegate?
public weak var reactionEventsDelegate: ReactionEventsDelegate?
public weak var teamEventsDelegate: TeamEventsDelegate?
public weak var subteamEventsDelegate: SubteamEventsDelegate?
public weak var teamProfileEventsDelegate: TeamProfileEventsDelegate?
internal var token = "SLACK_AUTH_TOKEN"
@@ -59,109 +60,112 @@ public class Client: WebSocketDelegate {
}
public var webAPI: SlackWebAPI {
return SlackWebAPI(client: self)
return SlackWebAPI(slackClient: self)
}
internal var webSocket: WebSocket?
internal var webSocket: WebSocket.Client?
internal var socket: Socket?
internal let api = NetworkInterface()
private var dispatcher: EventDispatcher?
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
internal var ping: Double?
internal var pong: Double?
internal var pingInterval: NSTimeInterval?
internal var timeout: NSTimeInterval?
internal var pingInterval: Double?
internal var timeout: Double?
internal var reconnect: Bool?
required public init(apiToken: String) {
self.token = apiToken
}
public func connect(pingInterval pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
public func connect(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, pingInterval: Double? = nil, timeout: Double? = nil, reconnect: Bool? = nil) {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
dispatcher = EventDispatcher(client: self)
webAPI.rtmStart(success: {
webAPI.rtmStart(simpleLatest: simpleLatest, noUnreads: noUnreads, mpimAware: mpimAware, success: {
(response) -> Void in
self.initialSetup(response)
self.initialSetup(json: response)
if let socketURL = response["url"] as? String {
let url = NSURL(string: socketURL)
self.webSocket = WebSocket(url: url!)
self.webSocket?.delegate = self
self.webSocket?.connect()
do {
let uri = try URI(socketURL)
self.webSocket = try WebSocket.Client(uri: uri, onConnect: {(socket) in
self.setupSocket(socket: socket)
if let pingInterval = self.pingInterval {
self.pingRTMServerAtInterval(interval: pingInterval)
}
})
try self.webSocket?.connect(uri.description)
} catch _ {
}
}
}, failure:nil)
}
/*TO-DO: Bug in Zewo/WebSocket
public func disconnect() {
webSocket?.disconnect()
}
_ = try? socket?.close()
}*/
//MARK: - Message send
//MARK: - RTM Message send
public func sendMessage(message: String, channelID: String) {
if (connected) {
if let data = formatMessageToSlackJsonString(msg: message, channel: channelID) {
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
if let data = formatMessageToSlackJsonString(message: message, channel: channelID) {
if let string = try? data.string() {
_ = try? socket?.send(string)
}
}
}
}
private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
private func formatMessageToSlackJsonString(message: String, channel: String) -> Data? {
let json: [String: Any] = [
"id": Time.slackTimestamp(),
"type": "message",
"channel": message.channel,
"text": message.msg.slackFormatEscaping()
"channel": channel,
"text": message.slackFormatEscaping()
]
addSentMessage(json)
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
return data
}
catch _ {
let bytes = try Jay().dataFromJson(json)
return Data(bytes)
} catch {
return nil
}
}
private func addSentMessage(dictionary: [String: AnyObject]) {
private func addSentMessage(dictionary: [String: Any]) {
var message = dictionary
let ts = message["id"] as? NSNumber
message.removeValueForKey("id")
message["ts"] = ts?.stringValue
let ts = message["id"] as? Int
message.removeValue(forKey:"id")
message["ts"] = "\(ts)"
message["user"] = self.authenticatedUser?.id
sentMessages[ts!.stringValue] = Message(message: message)
sentMessages["\(ts)"] = Message(message: message)
}
//MARK: - RTM Ping
private func pingRTMServerAtInterval(interval: NSTimeInterval) {
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
dispatch_after(delay, pingPongQueue, {
if self.connected && self.timeoutCheck() {
self.sendRTMPing()
self.pingRTMServerAtInterval(interval)
} else {
self.disconnect()
}
})
private func pingRTMServerAtInterval(interval: Double) {
co { [weak self] in
let weakSelf = self
repeat {
nap(for: interval)
weakSelf?.sendRTMPing()
} while weakSelf?.connected == true && weakSelf?.timeoutCheck() == true
//weakSelf?.disconnect()
}
}
private func sendRTMPing() {
if connected {
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
let json: [String: Any] = [
"id": Double.slackTimestamp(),
"type": "ping",
]
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
if let writePing = string as? String {
ping = json["id"] as? Double
webSocket?.writeString(writePing)
}
let data = try Jay().dataFromJson(json)
let string = try data.string()
ping = json["id"] as? Double
try socket?.send(string)
}
catch _ {
@@ -183,78 +187,42 @@ public class Client: WebSocketDelegate {
}
//MARK: - Client setup
internal func initialSetup(json: [String: AnyObject]) {
team = Team(team: json["team"] as? [String: AnyObject])
authenticatedUser = User(user: json["self"] as? [String: AnyObject])
authenticatedUser?.doNotDisturbStatus = DoNotDisturbStatus(status: json["dnd"] as? [String: AnyObject])
enumerateUsers(json["users"] as? Array)
enumerateChannels(json["channels"] as? Array)
enumerateGroups(json["groups"] as? Array)
enumerateMPIMs(json["mpims"] as? Array)
enumerateIMs(json["ims"] as? Array)
enumerateBots(json["bots"] as? Array)
enumerateSubteams(json["subteams"] as? [String: AnyObject])
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])
enumerateObjects(array: json["users"] as? Array) { (user) in self.addUser(aUser: user) }
enumerateObjects(array: json["channels"] as? Array) { (channel) in self.addChannel(aChannel: channel) }
enumerateObjects(array: json["groups"] as? Array) { (group) in self.addChannel(aChannel: group) }
enumerateObjects(array: json["mpims"] as? Array) { (mpim) in self.addChannel(aChannel: mpim) }
enumerateObjects(array: json["ims"] as? Array) { (ims) in self.addChannel(aChannel: ims) }
enumerateObjects(array: json["bots"] as? Array) { (bots) in self.addBot(aBot: bots) }
enumerateSubteams(subteams: json["subteams"] as? [String: Any])
}
internal func enumerateUsers(users: [AnyObject]?) {
if let users = users {
for user in users {
let u = User(user: user as? [String: AnyObject])
self.users[u!.id!] = u
}
private func addUser(aUser: [String: Any]) {
if let user = User(user: aUser), id = user.id {
users[id] = user
}
}
internal func enumerateChannels(channels: [AnyObject]?) {
if let channels = channels {
for channel in channels {
let c = Channel(channel: channel as? [String: AnyObject])
self.channels[c!.id!] = c
}
private func addChannel(aChannel: [String: Any]) {
if let channel = Channel(channel: aChannel), id = channel.id {
channels[id] = channel
}
}
internal func enumerateGroups(groups: [AnyObject]?) {
if let groups = groups {
for group in groups {
let g = Channel(channel: group as? [String: AnyObject])
self.channels[g!.id!] = g
}
private func addBot(aBot: [String: Any]) {
if let bot = Bot(bot: aBot), id = bot.id {
bots[id] = bot
}
}
internal func enumerateIMs(ims: [AnyObject]?) {
if let ims = ims {
for im in ims {
let i = Channel(channel: im as? [String: AnyObject])
self.channels[i!.id!] = i
}
}
}
internal func enumerateMPIMs(mpims: [AnyObject]?) {
if let mpims = mpims {
for mpim in mpims {
let m = Channel(channel: mpim as? [String: AnyObject])
self.channels[m!.id!] = m
}
}
}
internal func enumerateBots(bots: [AnyObject]?) {
if let bots = bots {
for bot in bots {
let b = Bot(bot: bot as? [String: AnyObject])
self.bots[b!.id!] = b
}
}
}
internal func enumerateSubteams(subteams: [String: AnyObject]?) {
private func enumerateSubteams(subteams: [String: Any]?) {
if let subteams = subteams {
if let all = subteams["all"] as? [[String: AnyObject]] {
if let all = subteams["all"] as? [Any] {
for item in all {
let u = UserGroup(userGroup: item)
let u = UserGroup(userGroup: item as? [String: Any])
self.userGroups[u!.id!] = u
}
}
@@ -267,18 +235,49 @@ public class Client: WebSocketDelegate {
}
}
// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = pingInterval {
pingRTMServerAtInterval(pingInterval)
// MARK: - Utilities
private func enumerateObjects(array: [Any]?, initalizer: ([String: Any])-> Void) {
if let array = array {
for object in array {
if let dictionary = object as? [String: Any] {
initalizer(dictionary)
}
}
}
}
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
// MARK: - WebSocket
private func setupSocket(socket: Socket) {
socket.onText {(message) in
self.websocketDidReceive(message: message)
}
socket.onPing { (data) in try socket.pong() }
socket.onPong { (data) in try socket.ping() }
socket.onClose{ (code: CloseCode?, reason: String?) in
self.websocketDidDisconnect(closeCode: code, error: reason)
}
self.socket = socket
}
private func websocketDidReceive(message: String) {
do {
let json = try Jay().jsonFromData(message.data.bytes)
if let event = json as? [String: Any] {
dispatch(event:event)
}
}
catch _ {
}
}
private func websocketDidDisconnect(closeCode: CloseCode?, error: String?) {
connected = false
authenticated = false
webSocket = nil
dispatcher = nil
socket = nil
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
@@ -286,20 +285,4 @@ public class Client: WebSocketDelegate {
}
}
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
guard let data = text.dataUsingEncoding(NSUTF8StringEncoding) else {
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
dispatcher?.dispatch(json)
}
}
catch _ {
}
}
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {}
}
+17 -19
View File
@@ -137,14 +137,13 @@ internal struct Event {
let fileID: String?
let presence: String?
let name: String?
let value: AnyObject?
let value: Any?
let plan: String?
let url: String?
let domain: String?
let emailDomain: String?
let reaction: String?
let replyTo: Double?
let reactions: [[String: AnyObject]]?
let edited: Edited?
let bot: Bot?
let channel: Channel?
@@ -160,7 +159,7 @@ internal struct Event {
let subteamID: String?
var profile: CustomProfile?
init(event:[String: AnyObject]) {
init(event:[String: Any]) {
if let eventType = event["type"] as? String {
type = EventType(rawValue:eventType)
} else {
@@ -186,41 +185,40 @@ internal struct Event {
emailDomain = event["email_domain"] as? String
reaction = event["reaction"] as? String
replyTo = event["reply_to"] as? Double
reactions = event["reactions"] as? [[String: AnyObject]]
bot = Bot(bot: event["bot"] as? [String: AnyObject])
edited = Edited(edited:event["edited"] as? [String: AnyObject])
dndStatus = DoNotDisturbStatus(status: event["dnd_status"] as? [String: AnyObject])
bot = Bot(bot: event["bot"] as? [String: Any])
edited = Edited(edited:event["edited"] as? [String: Any])
dndStatus = DoNotDisturbStatus(status: event["dnd_status"] as? [String: Any])
itemUser = event["item_user"] as? String
item = Item(item: event["item"] as? [String: AnyObject])
subteam = UserGroup(userGroup: event["subteam"] as? [String: AnyObject])
item = Item(item: event["item"] as? [String: Any])
subteam = UserGroup(userGroup: event["subteam"] as? [String: Any])
subteamID = event["subteam_id"] as? String
message = Message(message: event)
nestedMessage = Message(message: event["message"] as? [String: AnyObject])
profile = CustomProfile(profile: event["profile"] as? [String: AnyObject])
nestedMessage = Message(message: event["message"] as? [String: Any])
profile = CustomProfile(profile: event["profile"] as? [String: Any])
// Comment, Channel, User, and File can come across as Strings or Dictionaries
if (Comment(comment: event["comment"] as? [String: AnyObject])?.id == nil) {
if (Comment(comment: event["comment"] as? [String: Any])?.id == nil) {
comment = Comment(id: event["comment"] as? String)
} else {
comment = Comment(comment: event["comment"] as? [String: AnyObject])
comment = Comment(comment: event["comment"] as? [String: Any])
}
if (User(user: event["user"] as? [String: AnyObject])?.id == nil) {
if (User(user: event["user"] as? [String: Any])?.id == nil) {
user = User(id: event["user"] as? String)
} else {
user = User(user: event["user"] as? [String: AnyObject])
user = User(user: event["user"] as? [String: Any])
}
if (File(file: event["file"] as? [String: AnyObject])?.id == nil) {
if (File(file: event["file"] as? [String: Any])?.id == nil) {
file = File(id: event["file"] as? String)
} else {
file = File(file: event["file"] as? [String: AnyObject])
file = File(file: event["file"] as? [String: Any])
}
if (Channel(channel: event["channel"] as? [String: AnyObject])?.id == nil) {
if (Channel(channel: event["channel"] as? [String: Any])?.id == nil) {
channel = Channel(id: event["channel"] as? String)
} else {
channel = Channel(channel: event["channel"] as? [String: AnyObject])
channel = Channel(channel: event["channel"] as? [String: Any])
}
}
+14 -16
View File
@@ -21,26 +21,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public protocol SlackEventsDelegate {
public protocol SlackEventsDelegate: class {
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
func preferenceChanged(preference: String, value: Any)
func userChanged(user: User)
func presenceChanged(user: User?, presence: String?)
func manualPresenceChanged(user: User?, presence: String?)
func botEvent(bot: Bot)
}
public protocol MessageEventsDelegate {
public protocol MessageEventsDelegate: class {
func messageSent(message: Message)
func messageReceived(message: Message)
func messageChanged(message: Message)
func messageDeleted(message: Message?)
}
public protocol ChannelEventsDelegate {
public protocol ChannelEventsDelegate: class {
func userTyping(channel: Channel?, user: User?)
func channelMarked(channel: Channel, timestamp: String?)
func channelCreated(channel: Channel)
@@ -52,16 +50,16 @@ public protocol ChannelEventsDelegate {
func channelLeft(channel: Channel)
}
public protocol DoNotDisturbEventsDelegate {
public protocol DoNotDisturbEventsDelegate: class {
func doNotDisturbUpdated(dndStatus: DoNotDisturbStatus)
func doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User?)
}
public protocol GroupEventsDelegate {
public protocol GroupEventsDelegate: class {
func groupOpened(group: Channel)
}
public protocol FileEventsDelegate {
public protocol FileEventsDelegate: class {
func fileProcessed(file: File)
func fileMadePrivate(file: File)
func fileDeleted(file: File)
@@ -70,37 +68,37 @@ public protocol FileEventsDelegate {
func fileCommentDeleted(file: File, comment: Comment)
}
public protocol PinEventsDelegate {
public protocol PinEventsDelegate: class {
func itemPinned(item: Item?, channel: Channel?)
func itemUnpinned(item: Item?, channel: Channel?)
}
public protocol StarEventsDelegate {
public protocol StarEventsDelegate: class {
func itemStarred(item: Item, star: Bool)
}
public protocol ReactionEventsDelegate {
public protocol ReactionEventsDelegate: class {
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
}
public protocol TeamEventsDelegate {
public protocol TeamEventsDelegate: class {
func teamJoined(user: User)
func teamPlanChanged(plan: String)
func teamPreferencesChanged(preference: String, value: AnyObject)
func teamPreferencesChanged(preference: String, value: Any)
func teamNameChanged(name: String)
func teamDomainChanged(domain: String)
func teamEmailDomainChanged(domain: String)
func teamEmojiChanged()
}
public protocol SubteamEventsDelegate {
public protocol SubteamEventsDelegate: class {
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
}
public protocol TeamProfileEventsDelegate {
public protocol TeamProfileEventsDelegate: class {
func teamProfileChanged(profile: CustomProfile?)
func teamProfileDeleted(profile: CustomProfile?)
func teamProfileReordered(profile: CustomProfile?)
-649
View File
@@ -1,649 +0,0 @@
//
// EventHandler.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 class EventHandler {
let client: Client
required init(client: Client) {
self.client = client
}
//MARK: - Initial connection
func connected() {
client.connected = true
if let delegate = client.slackEventsDelegate {
delegate.clientConnected()
}
}
//MARK: - Pong
func pong(event: Event) {
client.pong = event.replyTo
}
//MARK: - Messages
func messageSent(event: Event) {
if let reply = event.replyTo, message = client.sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts {
message.ts = event.ts
message.text = event.text
client.channels[channel]?.messages[ts] = message
if let delegate = client.messageEventsDelegate {
delegate.messageSent(message)
}
}
}
func messageReceived(event: Event) {
if let channel = event.channel, message = event.message, id = channel.id, ts = message.ts {
client.channels[id]?.messages[ts] = message
if let delegate = client.messageEventsDelegate {
delegate.messageReceived(message)
}
}
}
func messageChanged(event: Event) {
if let id = event.channel?.id, nested = event.nestedMessage, ts = nested.ts {
client.channels[id]?.messages[ts] = nested
if let delegate = client.messageEventsDelegate {
delegate.messageChanged(nested)
}
}
}
func messageDeleted(event: Event) {
if let id = event.channel?.id, key = event.message?.deletedTs {
let message = client.channels[id]?.messages[key]
client.channels[id]?.messages.removeValueForKey(key)
if let delegate = client.messageEventsDelegate {
delegate.messageDeleted(message)
}
}
}
//MARK: - Channels
func userTyping(event: Event) {
if let channelID = event.channel?.id, userID = event.user?.id {
if let _ = client.channels[channelID] {
if (!client.channels[channelID]!.usersTyping.contains(userID)) {
client.channels[channelID]?.usersTyping.append(userID)
if let delegate = client.channelEventsDelegate {
delegate.userTyping(event.channel, user: event.user)
}
}
}
let timeout = dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC)))
dispatch_after(timeout, dispatch_get_main_queue()) {
if let index = self.client.channels[channelID]?.usersTyping.indexOf(userID) {
self.client.channels[channelID]?.usersTyping.removeAtIndex(index)
}
}
}
}
func channelMarked(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.lastRead = event.ts
if let delegate = client.channelEventsDelegate {
delegate.channelMarked(channel, timestamp: event.ts)
}
}
//TODO: Recalculate unreads
}
func channelCreated(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id] = channel
if let delegate = client.channelEventsDelegate {
delegate.channelCreated(channel)
}
}
}
func channelDeleted(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels.removeValueForKey(id)
if let delegate = client.channelEventsDelegate {
delegate.channelDeleted(channel)
}
}
}
func channelJoined(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id] = event.channel
if let delegate = client.channelEventsDelegate {
delegate.channelJoined(channel)
}
}
}
func channelLeft(event: Event) {
if let channel = event.channel, id = channel.id, userID = client.authenticatedUser?.id {
if let index = client.channels[id]?.members?.indexOf(userID) {
client.channels[id]?.members?.removeAtIndex(index)
if let delegate = client.channelEventsDelegate {
delegate.channelLeft(channel)
}
}
}
}
func channelRenamed(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.name = channel.name
if let delegate = client.channelEventsDelegate {
delegate.channelRenamed(channel)
}
}
}
func channelArchived(event: Event, archived: Bool) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.isArchived = archived
if let delegate = client.channelEventsDelegate {
delegate.channelArchived(channel)
}
}
}
func channelHistoryChanged(event: Event) {
if let channel = event.channel {
//TODO: Reload chat history if there are any cached messages before latest
if let delegate = client.channelEventsDelegate {
delegate.channelHistoryChanged(channel)
}
}
}
//MARK: - Do Not Disturb
func doNotDisturbUpdated(event: Event) {
if let dndStatus = event.dndStatus {
client.authenticatedUser?.doNotDisturbStatus = dndStatus
if let delegate = client.doNotDisturbEventsDelegate {
delegate.doNotDisturbUpdated(dndStatus)
}
}
}
func doNotDisturbUserUpdated(event: Event) {
if let dndStatus = event.dndStatus, user = event.user, id = user.id {
client.users[id]?.doNotDisturbStatus = dndStatus
if let delegate = client.doNotDisturbEventsDelegate {
delegate.doNotDisturbUserUpdated(dndStatus, user: user)
}
}
}
//MARK: - IM & Group Open/Close
func open(event: Event, open: Bool) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.isOpen = open
if let delegate = client.groupEventsDelegate {
delegate.groupOpened(channel)
}
}
}
//MARK: - Files
func processFile(event: Event) {
if let file = event.file, id = file.id {
if let comment = file.initialComment, commentID = comment.id {
if client.files[id]?.comments[commentID] == nil {
client.files[id]?.comments[commentID] = comment
}
}
client.files[id] = file
if let delegate = client.fileEventsDelegate {
delegate.fileProcessed(file)
}
}
}
func filePrivate(event: Event) {
if let file = event.file, id = file.id {
client.files[id]?.isPublic = false
if let delegate = client.fileEventsDelegate {
delegate.fileMadePrivate(file)
}
}
}
func deleteFile(event: Event) {
if let file = event.file, id = file.id {
if client.files[id] != nil {
client.files.removeValueForKey(id)
}
if let delegate = client.fileEventsDelegate {
delegate.fileDeleted(file)
}
}
}
func fileCommentAdded(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments[commentID] = comment
if let delegate = client.fileEventsDelegate {
delegate.fileCommentAdded(file, comment: comment)
}
}
}
func fileCommentEdited(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments[commentID]?.comment = comment.comment
if let delegate = client.fileEventsDelegate {
delegate.fileCommentEdited(file, comment: comment)
}
}
}
func fileCommentDeleted(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments.removeValueForKey(commentID)
if let delegate = client.fileEventsDelegate {
delegate.fileCommentDeleted(file, comment: comment)
}
}
}
//MARK: - Pins
func pinAdded(event: Event) {
if let id = event.channelID, item = event.item {
client.channels[id]?.pinnedItems.append(item)
if let delegate = client.pinEventsDelegate {
delegate.itemPinned(item, channel: client.channels[id])
}
}
}
func pinRemoved(event: Event) {
if let id = event.channelID {
if let pins = client.channels[id]?.pinnedItems.filter({$0 != event.item}) {
client.channels[id]?.pinnedItems = pins
}
if let delegate = client.pinEventsDelegate {
delegate.itemUnpinned(event.item, channel: client.channels[id])
}
}
}
//MARK: - Stars
func itemStarred(event: Event, star: Bool) {
if let item = event.item, type = item.type {
switch type {
case "message":
starMessage(item, star: star)
case "file":
starFile(item, star: star)
case "file_comment":
starComment(item)
default:
break
}
if let delegate = client.starEventsDelegate {
delegate.itemStarred(item, star: star)
}
}
}
func starMessage(item: Item, star: Bool) {
if let message = item.message, ts = message.ts, channel = item.channel {
if let _ = client.channels[channel]?.messages[ts] {
client.channels[channel]?.messages[ts]?.isStarred = star
}
}
}
func starFile(item: Item, star: Bool) {
if let file = item.file, id = file.id {
client.files[id]?.isStarred = star
if let stars = client.files[id]?.stars {
if star == true {
client.files[id]?.stars = stars + 1
} else {
if stars > 0 {
client.files[id]?.stars = stars - 1
}
}
}
}
}
func starComment(item: Item) {
if let file = item.file, id = file.id, comment = item.comment, commentID = comment.id {
client.files[id]?.comments[commentID] = comment
}
}
//MARK: - Reactions
func addedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = client.channels[channel]?.messages[ts] {
if (message.reactions[key]) == nil {
message.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
message.reactions[key]?.users[userID] = userID
}
}
}
case "file":
if let id = item.file?.id, file = client.files[id] {
if file.reactions[key] == nil {
client.files[id]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
client.files[id]?.reactions[key]?.users[userID] = userID
}
}
case "file_comment":
if let id = item.file?.id, file = client.files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] == nil {
client.files[id]?.comments[commentID]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
client.files[id]?.comments[commentID]?.reactions[key]?.users[userID] = userID
}
}
break
default:
break
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
func removedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = client.channels[channel]?.messages[ts] {
if (message.reactions[key]) != nil {
message.reactions[key]?.users.removeValueForKey(userID)
}
if (message.reactions[key]?.users.count == 0) {
message.reactions.removeValueForKey(key)
}
}
}
case "file":
if let itemFile = item.file, id = itemFile.id, file = client.files[id] {
if file.reactions[key] != nil {
client.files[id]?.reactions[key]?.users.removeValueForKey(userID)
}
if client.files[id]?.reactions[key]?.users.count == 0 {
client.files[id]?.reactions.removeValueForKey(key)
}
}
case "file_comment":
if let id = item.file?.id, file = client.files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] != nil {
client.files[id]?.comments[commentID]?.reactions[key]?.users.removeValueForKey(userID)
}
if client.files[id]?.comments[commentID]?.reactions[key]?.users.count == 0 {
client.files[id]?.comments[commentID]?.reactions.removeValueForKey(key)
}
}
break
default:
break
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
//MARK: - Preferences
func changePreference(event: Event) {
if let name = event.name {
client.authenticatedUser?.preferences?[name] = event.value
if let delegate = client.slackEventsDelegate, value = event.value {
delegate.preferenceChanged(name, value: value)
}
}
}
//Mark: - User Change
func userChange(event: Event) {
if let user = event.user, id = user.id {
let preferences = client.users[id]?.preferences
client.users[id] = user
client.users[id]?.preferences = preferences
if let delegate = client.slackEventsDelegate {
delegate.userChanged(user)
}
}
}
//MARK: - User Presence
func presenceChange(event: Event) {
if let user = event.user, id = user.id {
client.users[id]?.presence = event.presence
if let delegate = client.slackEventsDelegate {
delegate.presenceChanged(user, presence: event.presence)
}
}
}
//MARK: - Team
func teamJoin(event: Event) {
if let user = event.user, id = user.id {
client.users[id] = user
if let delegate = client.teamEventsDelegate {
delegate.teamJoined(user)
}
}
}
func teamPlanChange(event: Event) {
if let plan = event.plan {
client.team?.plan = plan
if let delegate = client.teamEventsDelegate {
delegate.teamPlanChanged(plan)
}
}
}
func teamPreferenceChange(event: Event) {
if let name = event.name {
client.team?.prefs?[name] = event.value
if let delegate = client.teamEventsDelegate, value = event.value {
delegate.teamPreferencesChanged(name, value: value)
}
}
}
func teamNameChange(event: Event) {
if let name = event.name {
client.team?.name = name
if let delegate = client.teamEventsDelegate {
delegate.teamNameChanged(name)
}
}
}
func teamDomainChange(event: Event) {
if let domain = event.domain {
client.team?.domain = domain
if let delegate = client.teamEventsDelegate {
delegate.teamDomainChanged(domain)
}
}
}
func emailDomainChange(event: Event) {
if let domain = event.emailDomain {
client.team?.emailDomain = domain
if let delegate = client.teamEventsDelegate {
delegate.teamEmailDomainChanged(domain)
}
}
}
func emojiChanged(event: Event) {
//TODO: Call emoji.list here
if let delegate = client.teamEventsDelegate {
delegate.teamEmojiChanged()
}
}
//MARK: - Bots
func bot(event: Event) {
if let bot = event.bot, id = bot.id {
client.bots[id] = bot
if let delegate = client.slackEventsDelegate {
delegate.botEvent(bot)
}
}
}
//MARK: - Subteams
func subteam(event: Event) {
if let subteam = event.subteam, id = subteam.id {
client.userGroups[id] = subteam
if let delegate = client.subteamEventsDelegate {
delegate.subteamEvent(subteam)
}
}
}
func subteamAddedSelf(event: Event) {
if let subteamID = event.subteamID, _ = client.authenticatedUser?.userGroups {
client.authenticatedUser?.userGroups![subteamID] = subteamID
if let delegate = client.subteamEventsDelegate {
delegate.subteamSelfAdded(subteamID)
}
}
}
func subteamRemovedSelf(event: Event) {
if let subteamID = event.subteamID {
client.authenticatedUser?.userGroups?.removeValueForKey(subteamID)
if let delegate = client.subteamEventsDelegate {
delegate.subteamSelfRemoved(subteamID)
}
}
}
//MARK: - Team Profiles
func teamProfileChange(event: Event) {
for user in client.users {
if let fields = event.profile?.fields {
for key in fields.keys {
client.users[user.0]?.profile?.customProfile?.fields[key]?.updateProfileField(fields[key])
}
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileChanged(event.profile)
}
}
func teamProfileDeleted(event: Event) {
for user in client.users {
if let id = event.profile?.fields.first?.0 {
client.users[user.0]?.profile?.customProfile?.fields[id] = nil
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileDeleted(event.profile)
}
}
func teamProfileReordered(event: Event) {
for user in client.users {
if let keys = event.profile?.fields.keys {
for key in keys {
client.users[user.0]?.profile?.customProfile?.fields[key]?.ordering = event.profile?.fields[key]?.ordering
}
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileReordered(event.profile)
}
}
//MARK: - Authenticated User
func manualPresenceChange(event: Event) {
client.authenticatedUser?.presence = event.presence
if let delegate = client.slackEventsDelegate {
delegate.manualPresenceChanged(client.authenticatedUser, presence: event.presence)
}
}
}
+99
View File
@@ -0,0 +1,99 @@
//
// Extensions.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 C7
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
public typealias Time=Double
public extension Double {
static func slackTimestamp() -> Double {
#if os(Linux)
return Double(time(nil))
#else
var clock: clock_serv_t = clock_serv_t()
var timeSpecBuffer: mach_timespec_t = mach_timespec_t(tv_sec: 0, tv_nsec: 0)
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock)
clock_get_time(clock, &timeSpecBuffer)
mach_port_deallocate(mach_task_self_, clock)
return Double(timeSpecBuffer.tv_sec) + Double(timeSpecBuffer.tv_nsec) * 0.000000001
#endif
}
}
internal extension String {
func slackFormatEscaping() -> String {
var escapedString = self
escapedString.replace(string: "&", with: "&amp;")
escapedString.replace(string: "<", with: "&lt;")
escapedString.replace(string: ">", with: "&gt;")
return escapedString
}
}
public extension String {
func contains(query: String, caseSensitive: Bool = false) -> Bool {
if query.isEmpty { return true }
let (s, q) = caseSensitive ? (self, query) : (self.lowercased(), query.lowercased())
var chars = s.characters; let qchars = q.characters
while !chars.isEmpty {
if chars.starts(with: qchars) { return true }
chars.removeFirst()
}
return false
}
func prefixedBy(query: String, caseSensitive: Bool = false) -> Bool {
let (s, q) = caseSensitive ? (self, query) : (self.lowercased(), query.lowercased())
return s.characters.starts(with: q.characters)
}
}
internal extension Array {
func objectArrayFromDictionaryArray<T>(intializer:([String: Any])->T?) -> [T] {
var returnValue = [T]()
for object in self {
if let dictionary = object as? [String: Any] {
if let value = intializer(dictionary) {
returnValue.append(value)
}
}
}
return returnValue
}
}
+4 -4
View File
@@ -62,7 +62,7 @@ public struct File {
internal(set) public var comments = [String: Comment]()
internal(set) public var reactions = [String: Reaction]()
public init?(file:[String: AnyObject]?) {
public init?(file:[String: Any]?) {
id = file?["id"] as? String
created = file?["created"] as? Int
name = file?["name"] as? String
@@ -95,12 +95,12 @@ public struct File {
channels = file?["channels"] as? [String]
groups = file?["groups"] as? [String]
ims = file?["ims"] as? [String]
initialComment = Comment(comment: file?["initial_comment"] as? [String: AnyObject])
initialComment = Comment(comment: file?["initial_comment"] as? [String: Any])
stars = file?["num_stars"] as? Int
isStarred = file?["is_starred"] as? Bool
pinnedTo = file?["pinned_to"] as? [String]
if let reactions = file?["reactions"] as? [[String: AnyObject]] {
self.reactions = Reaction.reactionsFromArray(reactions)
if let reactions = file?["reactions"] as? [Any] {
self.reactions = Reaction.reactionsFromArray(array: reactions)
}
}
+27 -30
View File
@@ -21,13 +21,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public enum ItemType: String {
case ChannelMessage = "C"
case PrivateGroupMessage = "G"
case File = "F"
case FileComments = "Fc"
}
public class Message {
public let type = "message"
@@ -39,7 +32,7 @@ public class Message {
internal(set) public var text: String?
public let botID: String?
public let username: String?
public let icons: [String: AnyObject]?
public let icons: [String: Any]?
public let deletedTs: String?
internal(set) var purpose: String?
internal(set) var topic: String?
@@ -53,9 +46,9 @@ public class Message {
public let comment: Comment?
public let file: File?
internal(set) public var reactions = [String: Reaction]()
internal(set) public var attachments: [Attachment] = []
internal(set) public var attachments: [Attachment]?
public init?(message: [String: AnyObject]?) {
public init?(message: [String: Any]?) {
subtype = message?["subtype"] as? String
ts = message?["ts"] as? String
user = message?["user"] as? String
@@ -64,7 +57,7 @@ public class Message {
text = message?["text"] as? String
botID = message?["bot_id"] as? String
username = message?["username"] as? String
icons = message?["icons"] as? [String: AnyObject]
icons = message?["icons"] as? [String: Any]
deletedTs = message?["deleted_ts"] as? String
purpose = message?["purpose"] as? String
topic = message?["topic"] as? String
@@ -75,36 +68,40 @@ public class Message {
itemType = message?["item_type"] as? String
isStarred = message?["is_starred"] as? Bool
pinnedTo = message?["pinned_to"] as? [String]
comment = Comment(comment: message?["comment"] as? [String: AnyObject])
file = File(file: message?["file"] as? [String: AnyObject])
reactions = messageReactions(message?["reactions"] as? [[String: AnyObject]])
attachments = messageAttachments(message?["attachments"] as? [[String: AnyObject]])
comment = Comment(comment: message?["comment"] as? [String: Any])
file = File(file: message?["file"] as? [String: Any])
reactions = messageReactions(reactions: message?["reactions"] as? [Any])
attachments = (message?["attachments"] as? [Any])?.objectArrayFromDictionaryArray(intializer: {(attachment) -> Attachment? in
return Attachment(attachment: attachment)
})
}
private func messageReactions(reactions: [[String: AnyObject]]?) -> [String: Reaction] {
internal init?(ts:String?) {
self.ts = ts
subtype = nil
user = nil
channel = nil
botID = nil
username = nil
icons = nil
deletedTs = nil
upload = nil
itemType = nil
comment = nil
file = nil
}
private func messageReactions(reactions: [Any]?) -> [String: Reaction] {
var returnValue = [String: Reaction]()
if let r = reactions {
for react in r {
if let reaction = Reaction(reaction: react), reactionName = reaction.name {
if let reaction = Reaction(reaction: react as? [String: Any]), reactionName = reaction.name {
returnValue[reactionName] = reaction
}
}
}
return returnValue
}
private func messageAttachments(attachments: [[String: AnyObject]]?) -> [Attachment] {
var returnValue:[Attachment] = []
if let a = attachments {
for attachment in a {
if let attachment = Attachment(attachment: attachment) {
returnValue.append(attachment)
}
}
}
return returnValue
}
}
extension Message: Equatable {}
+90 -70
View File
@@ -21,107 +21,127 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import C7
import HTTPSClient
import Jay
internal struct NetworkInterface {
private let apiUrl = "https://slack.com/api/"
private let client: HTTPSClient.Client?
internal func request(endpoint: SlackAPIEndpoint, token: String, parameters: [String: AnyObject]?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?token=\(token)"
if let params = parameters {
requestString += requestStringFromParameters(params)
init() {
do {
self.client = try Client(uri: URI("https://slack.com"))
} catch {
self.client = nil
}
let request = NSURLRequest(URL: NSURL(string: requestString)!)
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
guard let data = data else {
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
if (result["ok"] as! Bool == true) {
successClosure(result)
} else {
if let errorString = result["error"] as? String {
throw ErrorDispatcher.dispatch(errorString)
} else {
throw SlackError.UnknownError
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
}
}.resume()
}
internal func uploadRequest(token: String, data: NSData, parameters: [String: AnyObject]?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(SlackAPIEndpoint.FilesUpload.rawValue)?token=\(token)"
internal func request(endpoint: SlackAPIEndpoint, token: String, parameters: [String: Any]?, successClosure: ([String: Any])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?token=\(token)"
if let params = parameters {
requestString = requestString + requestStringFromParameters(params)
requestString += requestStringFromParameters(parameters: params)
}
let request = NSMutableURLRequest(URL: NSURL(string: requestString)!)
request.HTTPMethod = "POST"
do {
var response: Response?
response = try client?.get(requestString)
let data = try response?.body.becomeBuffer()
if let data = data {
let json = try Jay().jsonFromData(data.bytes)
if let result = json as? [String: Any] {
if (result["ok"] as? Bool == true) {
successClosure(result)
} else {
if let errorString = result["error"] as? String {
throw ErrorDispatcher.dispatch(error: errorString)
} else {
throw SlackError.UnknownError
}
}
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
}
}
internal func uploadRequest(token: String, data: Data, parameters: [String: Any]?, successClosure: ([String: Any])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(SlackAPIEndpoint.FilesUpload.rawValue)?token=\(token)"
if let params = parameters {
requestString = requestString + requestStringFromParameters(parameters: params)
}
let boundaryConstant = randomBoundary()
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let contentType:Header = ["multipart/form-data; boundary=" + boundaryConstant]
let boundaryStart = "--\(boundaryConstant)\r\n"
let boundaryEnd = "--\(boundaryConstant)--\r\n"
let contentDispositionString = "Content-Disposition: form-data; name=\"file\"; filename=\"\(parameters!["filename"])\"\r\n"
let contentTypeString = "Content-Type: \(parameters!["filetype"])\r\n\r\n"
let requestBodyData : NSMutableData = NSMutableData()
requestBodyData.appendData(boundaryStart.dataUsingEncoding(NSUTF8StringEncoding)!)
requestBodyData.appendData(contentDispositionString.dataUsingEncoding(NSUTF8StringEncoding)!)
requestBodyData.appendData(contentTypeString.dataUsingEncoding(NSUTF8StringEncoding)!)
requestBodyData.appendData(data)
requestBodyData.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
requestBodyData.appendData(boundaryEnd.dataUsingEncoding(NSUTF8StringEncoding)!)
var requestBodyData = Data()
requestBodyData.append(contentsOf: boundaryStart.data.bytes)
requestBodyData.append(contentsOf: contentDispositionString.data.bytes)
requestBodyData.append(contentsOf: contentTypeString.data.bytes)
requestBodyData.append(contentsOf: data)
requestBodyData.append(contentsOf: "\r\n".data.bytes)
requestBodyData.append(contentsOf: boundaryEnd.data.bytes)
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.HTTPBody = requestBodyData
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
guard let data = data else {
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
if (result["ok"] as! Bool == true) {
successClosure(result)
} else {
if let errorString = result["error"] as? String {
throw ErrorDispatcher.dispatch(errorString)
let header: Headers = ["Content-Type":contentType]
do {
var response: Response?
response = try client?.post(requestString, headers: header, body: requestBodyData)
let data = try response?.body.becomeBuffer()
if let data = data {
let json = try Jay().jsonFromData(data.bytes)
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 ErrorDispatcher.dispatch(error: errorString)
} else {
throw SlackError.UnknownError
}
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
}
}.resume()
} 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())
return "slackkit.boundary.\(arc4random())\(arc4random())"
}
private func requestStringFromParameters(parameters: [String: AnyObject]) -> String {
private func requestStringFromParameters(parameters: [String: Any]) -> String {
var requestString = ""
for key in parameters.keys {
if let value = parameters[key] as? String, encodedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet()) {
requestString += "&\(key)=\(encodedValue)"
if let value = parameters[key] as? String {
do {
let encodedValue = try value.percentEncoded(allowing: .uriQueryAllowed)
requestString += "&\(key)=\(encodedValue)"
} catch _ {
print("Error encoding parameters.")
}
} else if let value = parameters[key] as? Int {
requestString += "&\(key)=\(value)"
} else if let value = parameters[key] as? Bool {
requestString += "&\(key)=\(value)"
}
}
+131 -124
View File
@@ -21,7 +21,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import C7
import Jay
internal enum SlackAPIEndpoint: String {
case APITest = "api.test"
@@ -103,15 +104,22 @@ public class SlackWebAPI {
case IM = "im"
}
private let client: Client
private let networkInterface: NetworkInterface
private let token: String
required public init(client: Client) {
self.client = client
init(networkInterface: NetworkInterface, token: String) {
self.networkInterface = networkInterface
self.token = token
}
convenience public init(slackClient: SlackClient) {
self.init(networkInterface: slackClient.api, token: slackClient.token)
}
//MARK: - RTM
public func rtmStart(success success: ((response: [String: AnyObject])->Void)?, failure: FailureClosure?) {
client.api.request(.RTMStart, token: client.token, parameters: nil, successClosure: {
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(endpoint: .RTMStart, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(response: response)
}) {(error) -> Void in
@@ -121,7 +129,7 @@ public class SlackWebAPI {
//MARK: - Auth Test
public func authenticationTest(success: ((authenticated: Bool)->Void)?, failure: FailureClosure?) {
client.api.request(.AuthTest, token: client.token, parameters: nil, successClosure: {
networkInterface.request(endpoint: .AuthTest, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(authenticated: true)
}) {(error) -> Void in
@@ -130,8 +138,8 @@ public class SlackWebAPI {
}
//MARK: - Channels
public func channelHistory(id: String, latest: String = "\(NSDate().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: {
public func channelHistory(id: String, latest: String = "\(Time.slackTimestamp())", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
history(endpoint: .ChannelsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
}) {(error) -> Void in
@@ -140,7 +148,7 @@ public class SlackWebAPI {
}
public func channelInfo(id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
info(.ChannelsInfo, type:ChannelType.Channel, id: id, success: {
info(endpoint: .ChannelsInfo, type:ChannelType.Channel, id: id, success: {
(channel) -> Void in
success?(channel: channel)
}) { (error) -> Void in
@@ -148,8 +156,8 @@ public class SlackWebAPI {
}
}
public func channelsList(excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
list(.ChannelsList, type:ChannelType.Channel, excludeArchived: excludeArchived, success: {
public func channelsList(excludeArchived: Bool = false, success: ((channels: [Any]?)->Void)?, failure: FailureClosure?) {
list(endpoint: .ChannelsList, type:ChannelType.Channel, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels: channels)
}) {(error) -> Void in
@@ -158,7 +166,7 @@ public class SlackWebAPI {
}
public func markChannel(channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
mark(.ChannelsMark, channel: channel, timestamp: timestamp, success: {
mark(endpoint: .ChannelsMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(ts:timestamp)
}) {(error) -> Void in
@@ -167,7 +175,7 @@ public class SlackWebAPI {
}
public func setChannelPurpose(channel: String, purpose: String, success: ((purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.ChannelsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
setInfo(endpoint: .ChannelsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
(purposeSet) -> Void in
success?(purposeSet: purposeSet)
}) { (error) -> Void in
@@ -176,7 +184,7 @@ public class SlackWebAPI {
}
public func setChannelTopic(channel: String, topic: String, success: ((topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.ChannelsSetTopic, type: .Topic, channel: channel, text: topic, success: {
setInfo(endpoint: .ChannelsSetTopic, type: .Topic, channel: channel, text: topic, success: {
(topicSet) -> Void in
success?(topicSet: topicSet)
}) {(error) -> Void in
@@ -186,17 +194,17 @@ public class SlackWebAPI {
//MARK: - Messaging
public func deleteMessage(channel: String, ts: String, success: ((deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, "ts": ts]
client.api.request(.ChatDelete, token: client.token, parameters: parameters, successClosure: { (response) -> Void in
let parameters: [String: Any] = ["channel": channel, "ts": ts]
networkInterface.request(endpoint: .ChatDelete, token: token, parameters: parameters, successClosure: { (response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
failure?(error: error)
}
}
public func sendMessage(channel: String, text: String, username: String? = nil, asUser: Bool = false, parse: ParseMode = .Full, linkNames: Bool = false, attachments: [Attachment?]? = nil, unfurlLinks: Bool = false, unfurlMedia: Bool = false, iconURL: String? = nil, iconEmoji: String? = nil, success: (((ts: String?, channel: String?))->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["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]
client.api.request(.ChatPostMessage, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
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: attachments), "icon_url":iconURL, "icon_emoji":iconEmoji]
networkInterface.request(endpoint: .ChatPostMessage, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) -> Void in
@@ -205,8 +213,8 @@ public class SlackWebAPI {
}
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: AnyObject?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping(), "parse": parse.rawValue, "link_names": linkNames, "attachments":encodeAttachments(attachments)]
client.api.request(.ChatUpdate, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping(), "parse": parse.rawValue, "link_names": linkNames, "attachments":encodeAttachments(attachments: attachments)]
networkInterface.request(endpoint: .ChatUpdate, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(updated: true)
}) {(error) -> Void in
@@ -216,8 +224,8 @@ public class SlackWebAPI {
//MARK: - Do Not Disturb
public func dndInfo(user: String? = nil, success: ((status: DoNotDisturbStatus?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["user": user]
client.api.request(.DNDInfo, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["user": user]
networkInterface.request(endpoint: .DNDInfo, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(status: DoNotDisturbStatus(status: response))
}) {(error) -> Void in
@@ -226,20 +234,20 @@ public class SlackWebAPI {
}
public func dndTeamInfo(users: [String]? = nil, success: ((statuses: [String: DoNotDisturbStatus]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["users":users?.joinWithSeparator(",")]
client.api.request(.DNDTeamInfo, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["users":users?.joined(separator: ",")]
networkInterface.request(endpoint: .DNDTeamInfo, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(statuses: self.enumerateDNDStauses(response["users"] as? [String: AnyObject]))
success?(statuses: self.enumerateDNDStauses(statuses: response["users"] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
}
//MARK: - Emoji
public func emojiList(success: ((emojiList: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
client.api.request(.EmojiList, token: client.token, parameters: nil, successClosure: {
public func emojiList(success: ((emojiList: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(endpoint: .EmojiList, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(emojiList: response["emoji"] as? [String: AnyObject])
success?(emojiList: response["emoji"] as? [String: Any])
}) { (error) -> Void in
failure?(error: error)
}
@@ -247,8 +255,8 @@ public class SlackWebAPI {
//MARK: - Files
public func deleteFile(fileID: String, success: ((deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["file":fileID]
client.api.request(.FilesDelete, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["file":fileID]
networkInterface.request(endpoint: .FilesDelete, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
@@ -256,11 +264,11 @@ public class SlackWebAPI {
}
}
public func uploadFile(file: NSData, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((file: File?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "filename": filename, "filetype":filetype, "title":title, "initial_comment":initialComment, "channels":channels?.joinWithSeparator(",")]
client.api.uploadRequest(client.token, data: file, parameters: filterNilParameters(parameters), successClosure: {
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: token, data: file, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(file: File(file: response["file"] as? [String: AnyObject]))
success?(file: File(file: response["file"] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
@@ -268,28 +276,28 @@ public class SlackWebAPI {
//MARK: - File Comments
public func addFileComment(fileID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "comment":comment.slackFormatEscaping()]
client.api.request(.FilesCommentsAdd, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["file":fileID, "comment":comment.slackFormatEscaping()]
networkInterface.request(endpoint: .FilesCommentsAdd, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
success?(comment: Comment(comment: response["comment"] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
}
public func editFileComment(fileID: String, commentID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "id":commentID, "comment":comment.slackFormatEscaping()]
client.api.request(.FilesCommentsEdit, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["file":fileID, "id":commentID, "comment":comment.slackFormatEscaping()]
networkInterface.request(endpoint: .FilesCommentsEdit, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
success?(comment: Comment(comment: response["comment"] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
}
public func deleteFileComment(fileID: String, commentID: String, success: ((deleted: Bool?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "id": commentID]
client.api.request(.FilesCommentsDelete, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["file":fileID, "id": commentID]
networkInterface.request(endpoint: .FilesCommentsDelete, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
@@ -299,7 +307,7 @@ public class SlackWebAPI {
//MARK: - Groups
public func closeGroup(groupID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
close(.GroupsClose, channelID: groupID, success: {
close(endpoint: .GroupsClose, channelID: groupID, success: {
(closed) -> Void in
success?(closed:closed)
}) {(error) -> Void in
@@ -307,8 +315,8 @@ public class SlackWebAPI {
}
}
public func groupHistory(id: String, latest: String = "\(NSDate().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: {
public func groupHistory(id: String, latest: String = "\(Time.slackTimestamp())", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
history(endpoint: .GroupsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
}) {(error) -> Void in
@@ -317,7 +325,7 @@ public class SlackWebAPI {
}
public func groupInfo(id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
info(.GroupsInfo, type:ChannelType.Group, id: id, success: {
info(endpoint: .GroupsInfo, type:ChannelType.Group, id: id, success: {
(channel) -> Void in
success?(channel: channel)
}) {(error) -> Void in
@@ -325,8 +333,8 @@ public class SlackWebAPI {
}
}
public func groupsList(excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
list(.GroupsList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
public func groupsList(excludeArchived: Bool = false, success: ((channels: [Any]?)->Void)?, failure: FailureClosure?) {
list(endpoint: .GroupsList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels: channels)
}) {(error) -> Void in
@@ -335,7 +343,7 @@ public class SlackWebAPI {
}
public func markGroup(channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
mark(.GroupsMark, channel: channel, timestamp: timestamp, success: {
mark(endpoint: .GroupsMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(ts: timestamp)
}) {(error) -> Void in
@@ -344,8 +352,8 @@ public class SlackWebAPI {
}
public func openGroup(channel: String, success: ((opened: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["channel":channel]
client.api.request(.GroupsOpen, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["channel":channel]
networkInterface.request(endpoint: .GroupsOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(opened: true)
}) {(error) -> Void in
@@ -354,7 +362,7 @@ public class SlackWebAPI {
}
public func setGroupPurpose(channel: String, purpose: String, success: ((purposeSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.GroupsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
setInfo(endpoint: .GroupsSetPurpose, type: .Purpose, channel: channel, text: purpose, success: {
(purposeSet) -> Void in
success?(purposeSet: purposeSet)
}) {(error) -> Void in
@@ -363,7 +371,7 @@ public class SlackWebAPI {
}
public func setGroupTopic(channel: String, topic: String, success: ((topicSet: Bool)->Void)?, failure: FailureClosure?) {
setInfo(.GroupsSetTopic, type: .Topic, channel: channel, text: topic, success: {
setInfo(endpoint: .GroupsSetTopic, type: .Topic, channel: channel, text: topic, success: {
(topicSet) -> Void in
success?(topicSet: topicSet)
}) {(error) -> Void in
@@ -373,7 +381,7 @@ public class SlackWebAPI {
//MARK: - IM
public func closeIM(channel: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
close(.IMClose, channelID: channel, success: {
close(endpoint: .IMClose, channelID: channel, success: {
(closed) -> Void in
success?(closed: closed)
}) {(error) -> Void in
@@ -381,8 +389,8 @@ public class SlackWebAPI {
}
}
public func imHistory(id: String, latest: String = "\(NSDate().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: {
public func imHistory(id: String, latest: String = "\(Time.slackTimestamp())", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
history(endpoint: .IMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
}) {(error) -> Void in
@@ -390,8 +398,8 @@ public class SlackWebAPI {
}
}
public func imsList(excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
list(.IMList, type:ChannelType.IM, excludeArchived: excludeArchived, success: {
public func imsList(excludeArchived: Bool = false, success: ((channels: [Any]?)->Void)?, failure: FailureClosure?) {
list(endpoint: .IMList, type:ChannelType.IM, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels: channels)
}) {(error) -> Void in
@@ -400,7 +408,7 @@ public class SlackWebAPI {
}
public func markIM(channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
mark(.IMMark, channel: channel, timestamp: timestamp, success: {
mark(endpoint: .IMMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(ts: timestamp)
}) {(error) -> Void in
@@ -409,10 +417,10 @@ public class SlackWebAPI {
}
public func openIM(userID: String, success: ((imID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["user":userID]
client.api.request(.IMOpen, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["user":userID]
networkInterface.request(endpoint: .IMOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
let group = response["channel"] as? [String: AnyObject]
let group = response["channel"] as? [String: Any]
success?(imID: group?["id"] as? String)
}) {(error) -> Void in
failure?(error: error)
@@ -421,7 +429,7 @@ public class SlackWebAPI {
//MARK: - MPIM
public func closeMPIM(channel: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
close(.MPIMClose, channelID: channel, success: {
close(endpoint: .MPIMClose, channelID: channel, success: {
(closed) -> Void in
success?(closed: closed)
}) {(error) -> Void in
@@ -429,8 +437,8 @@ public class SlackWebAPI {
}
}
public func mpimHistory(id: String, latest: String = "\(NSDate().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: {
public func mpimHistory(id: String, latest: String = "\(Time.slackTimestamp())", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
history(endpoint: .MPIMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
}) {(error) -> Void in
@@ -438,8 +446,8 @@ public class SlackWebAPI {
}
}
public func mpimsList(excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
list(.MPIMList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
public func mpimsList(excludeArchived: Bool = false, success: ((channels: [Any]?)->Void)?, failure: FailureClosure?) {
list(endpoint: .MPIMList, type:ChannelType.Group, excludeArchived: excludeArchived, success: {
(channels) -> Void in
success?(channels: channels)
}) {(error) -> Void in
@@ -448,7 +456,7 @@ public class SlackWebAPI {
}
public func markMPIM(channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
mark(.MPIMMark, channel: channel, timestamp: timestamp, success: {
mark(endpoint: .MPIMMark, channel: channel, timestamp: timestamp, success: {
(ts) -> Void in
success?(ts: timestamp)
}) {(error) -> Void in
@@ -457,10 +465,10 @@ public class SlackWebAPI {
}
public func openMPIM(userIDs: [String], success: ((mpimID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["users":userIDs.joinWithSeparator(",")]
client.api.request(.MPIMOpen, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["users":userIDs.joined(separator: ",")]
networkInterface.request(endpoint: .MPIMOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
let group = response["group"] as? [String: AnyObject]
let group = response["group"] as? [String: Any]
success?(mpimID: group?["id"] as? String)
}) {(error) -> Void in
failure?(error: error)
@@ -469,7 +477,7 @@ public class SlackWebAPI {
//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: {
pin(endpoint: .PinsAdd, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {
(ok) -> Void in
success?(pinned: ok)
}) {(error) -> Void in
@@ -478,7 +486,7 @@ public class SlackWebAPI {
}
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: {
pin(endpoint: .PinsRemove, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {
(ok) -> Void in
success?(unpinned: ok)
}) {(error) -> Void in
@@ -487,8 +495,8 @@ public class SlackWebAPI {
}
private func pin(endpoint: SlackAPIEndpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel":channel, "file":file, "file_comment":fileComment, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["channel":channel, "file":file, "file_comment":fileComment, "timestamp":timestamp]
networkInterface.request(endpoint: endpoint, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}){(error) -> Void in
@@ -499,7 +507,7 @@ public class SlackWebAPI {
//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: {
react(endpoint: .ReactionsAdd, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(reacted: ok)
}) {(error) -> Void in
@@ -509,7 +517,7 @@ public class SlackWebAPI {
// 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: {
react(endpoint: .ReactionsRemove, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(unreacted: ok)
}) {(error) -> Void in
@@ -518,8 +526,8 @@ public class SlackWebAPI {
}
private func react(endpoint: SlackAPIEndpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["name":name, "file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["name":name, "file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint: endpoint, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -530,7 +538,7 @@ public class SlackWebAPI {
//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: {
star(endpoint: .StarsAdd, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(starred: ok)
}) {(error) -> Void in
@@ -540,7 +548,7 @@ public class SlackWebAPI {
// 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: {
star(endpoint: .StarsRemove, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {
(ok) -> Void in
success?(unstarred: ok)
}) {(error) -> Void in
@@ -549,8 +557,8 @@ public class SlackWebAPI {
}
private func star(endpoint: SlackAPIEndpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
let parameters: [String: Any?] = ["file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint: endpoint, token: token, parameters: filterNilParameters(parameters: parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -560,10 +568,10 @@ public class SlackWebAPI {
//MARK: - Team
public func teamInfo(success: ((info: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
client.api.request(.TeamInfo, token: client.token, parameters: nil, successClosure: {
public func teamInfo(success: ((info: [String: Any]?)->Void)?, failure: FailureClosure?) {
networkInterface.request(endpoint: .TeamInfo, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(info: response["team"] as? [String: AnyObject])
success?(info: response["team"] as? [String: Any])
}) {(error) -> Void in
failure?(error: error)
}
@@ -571,8 +579,8 @@ public class SlackWebAPI {
//MARK: - Users
public func userPresence(user: String, success: ((presence: String?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["user":user]
client.api.request(.UsersGetPresence, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["user":user]
networkInterface.request(endpoint: .UsersGetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(presence: response["presence"] as? String)
}){(error) -> Void in
@@ -581,27 +589,27 @@ public class SlackWebAPI {
}
public func userInfo(id: String, success: ((user: User?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["user":id]
client.api.request(.UsersInfo, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["user":id]
networkInterface.request(endpoint: .UsersInfo, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(user: User(user: response["user"] as? [String: AnyObject]))
success?(user: User(user: response["user"] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
}
public func usersList(includePresence: Bool = false, success: ((userList: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["presence":includePresence]
client.api.request(.UsersList, token: client.token, parameters: parameters, successClosure: {
public func usersList(includePresence: Bool = false, success: ((userList: [String: Any]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence":includePresence]
networkInterface.request(endpoint: .UsersList, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(userList: response["members"] as? [[String: AnyObject]])
success?(userList: response["members"] as? [String: Any])
}){(error) -> Void in
failure?(error: error)
}
}
public func setUserActive(success: ((success: Bool)->Void)?, failure: FailureClosure?) {
client.api.request(.UsersSetActive, token: client.token, parameters: nil, successClosure: {
networkInterface.request(endpoint: .UsersSetActive, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(success: true)
}) {(error) -> Void in
@@ -610,8 +618,8 @@ public class SlackWebAPI {
}
public func setUserPresence(presence: Presence, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["presence":presence.rawValue]
client.api.request(.UsersSetPresence, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["presence":presence.rawValue]
networkInterface.request(endpoint: .UsersSetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(success:true)
}) {(error) -> Void in
@@ -621,8 +629,8 @@ public class SlackWebAPI {
//MARK: - Channel Utilities
private func close(endpoint: SlackAPIEndpoint, channelID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel":channelID]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["channel":channelID]
networkInterface.request(endpoint: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(closed: true)
}) {(error) -> Void in
@@ -630,9 +638,9 @@ public class SlackWebAPI {
}
}
private func history(endpoint: SlackAPIEndpoint, id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": id, "latest": latest, "oldest": oldest, "inclusive":inclusive, "count":count, "unreads":unreads]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
private func history(endpoint: SlackAPIEndpoint, id: String, latest: String = "\(Time.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: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(history: History(history: response))
}) {(error) -> Void in
@@ -641,28 +649,28 @@ public class SlackWebAPI {
}
private func info(endpoint: SlackAPIEndpoint, type: ChannelType, id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": id]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["channel": id]
networkInterface.request(endpoint: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(channel: Channel(channel: response[type.rawValue] as? [String: AnyObject]))
success?(channel: Channel(channel: response[type.rawValue] as? [String: Any]))
}) {(error) -> Void in
failure?(error: error)
}
}
private func list(endpoint: SlackAPIEndpoint, type: ChannelType, excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["exclude_archived": excludeArchived]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
private func list(endpoint: SlackAPIEndpoint, type: ChannelType, excludeArchived: Bool = false, success: ((channels: [Any]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["exclude_archived": excludeArchived]
networkInterface.request(endpoint: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(channels: response[type.rawValue+"s"] as? [[String: AnyObject]])
success?(channels: response[type.rawValue+"s"] as? [Any])
}) {(error) -> Void in
failure?(error: error)
}
}
private func mark(endpoint: SlackAPIEndpoint, channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, "ts": timestamp]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["channel": channel, "ts": timestamp]
networkInterface.request(endpoint: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(ts: timestamp)
}) {(error) -> Void in
@@ -671,8 +679,8 @@ public class SlackWebAPI {
}
private func setInfo(endpoint: SlackAPIEndpoint, type: InfoType, channel: String, text: String, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, type.rawValue: text]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
let parameters: [String: Any] = ["channel": channel, type.rawValue: text]
networkInterface.request(endpoint: endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(success: true)
}) {(error) -> Void in
@@ -681,8 +689,8 @@ public class SlackWebAPI {
}
//MARK: - Filter Nil Parameters
private func filterNilParameters(parameters: [String: AnyObject?]) -> [String: AnyObject] {
var finalParameters = [String: AnyObject]()
private func filterNilParameters(parameters: [String: Any?]) -> [String: Any] {
var finalParameters = [String: Any]()
for key in parameters.keys {
if parameters[key] != nil {
finalParameters[key] = parameters[key]!
@@ -692,17 +700,16 @@ public class SlackWebAPI {
}
//MARK: - Encode Attachments
private func encodeAttachments(attachments: [Attachment?]?) -> NSString? {
private func encodeAttachments(attachments: [Attachment?]?) -> String? {
if let attachments = attachments {
var attachmentArray: [[String: AnyObject]] = []
var attachmentArray: [Any] = []
for attachment in attachments {
if let attachment = attachment {
attachmentArray.append(attachment.dictionary())
}
}
do {
let data = try NSJSONSerialization.dataWithJSONObject(attachmentArray, options: NSJSONWritingOptions.PrettyPrinted)
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
let string = try Jay().dataFromJson(attachmentArray).string()
return string
} catch _ {
@@ -712,11 +719,11 @@ public class SlackWebAPI {
}
//MARK: - Enumerate Do Not Distrub Status
private func enumerateDNDStauses(statuses: [String: AnyObject]?) -> [String: DoNotDisturbStatus] {
private func enumerateDNDStauses(statuses: [String: Any]?) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
if let keys = statuses?.keys {
for key in keys {
retVal[key] = DoNotDisturbStatus(status: statuses?[key] as? [String: AnyObject])
retVal[key] = DoNotDisturbStatus(status: statuses?[key] as? [String: Any])
}
}
return retVal
@@ -21,9 +21,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public enum SlackError: ErrorType {
public enum SlackError: ErrorProtocol {
case AccountInactive
case AlreadyArchived
case AlreadyInChannel
+5 -5
View File
@@ -29,20 +29,20 @@ public struct Team {
internal(set) public var emailDomain: String?
internal(set) public var messageEditWindowMinutes: Int?
internal(set) public var overStorageLimit: Bool?
internal(set) public var prefs: [String: AnyObject]?
internal(set) public var prefs: [String: Any]?
internal(set) public var plan: String?
internal(set) public var icon: TeamIcon?
internal init?(team: [String: AnyObject]?) {
internal init?(team: [String: Any]?) {
id = team?["id"] as! String
name = team?["name"] as? String
domain = team?["domain"] as? String
emailDomain = team?["email_domain"] as? String
messageEditWindowMinutes = team?["msg_edit_window_mins"] as? Int
overStorageLimit = team?["over_storage_limit"] as? Bool
prefs = team?["prefs"] as? [String: AnyObject]
prefs = team?["prefs"] as? [String: Any]
plan = team?["plan"] as? String
icon = TeamIcon(icon: team?["icon"] as? [String: AnyObject])
icon = TeamIcon(icon: team?["icon"] as? [String: Any])
}
}
@@ -56,7 +56,7 @@ public struct TeamIcon {
internal(set) public var imageOriginal: String?
internal(set) public var imageDefault: Bool?
internal init?(icon: [String: AnyObject]?) {
internal init?(icon: [String: Any]?) {
image34 = icon?["image_34"] as? String
image44 = icon?["image_44"] as? String
image68 = icon?["image_68"] as? String
+32 -31
View File
@@ -21,14 +21,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
// MARK: - Edited
public struct Edited {
public let user: String?
public let ts: String?
internal init?(edited:[String: AnyObject]?) {
internal init?(edited:[String: Any]?) {
user = edited?["user"] as? String
ts = edited?["ts"] as? String
}
@@ -36,17 +35,17 @@ public struct Edited {
// MARK: - History
public struct History {
internal(set) public var latest: NSDate?
internal(set) public var latest: Double?
internal(set) public var messages = [Message]()
public let hasMore: Bool?
internal init?(history: [String: AnyObject]?) {
internal init?(history: [String: Any]?) {
if let latestStr = history?["latest"] as? String, latestDouble = Double(latestStr) {
latest = NSDate(timeIntervalSince1970: NSTimeInterval(latestDouble))
latest = latestDouble
}
if let msgs = history?["messages"] as? [[String: AnyObject]] {
if let msgs = history?["messages"] as? [Any] {
for message in msgs {
if let message = Message(message: message) {
if let message = Message(message: message as? [String: Any]) {
messages.append(message)
}
}
@@ -60,7 +59,7 @@ public struct Reaction {
public let name: String?
internal(set) public var users = [String: String]()
internal init?(reaction:[String: AnyObject]?) {
internal init?(reaction:[String: Any]?) {
name = reaction?["name"] as? String
}
@@ -74,17 +73,19 @@ public struct Reaction {
self.users = users
}
static func reactionsFromArray(array: [[String: AnyObject]]) -> [String: Reaction] {
static func reactionsFromArray(array: [Any]) -> [String: Reaction] {
var reactions = [String: Reaction]()
var userDictionary = [String: String]()
for reaction in array {
if let users = reaction["users"] as? [String] {
for user in users {
userDictionary[user] = user
for r in array {
if let reaction = r as? [String: Any] {
if let users = reaction["users"] as? [String] {
for user in users {
userDictionary[user] = user
}
}
if let name = reaction["name"] as? String {
reactions[name] = Reaction(name: name, users: userDictionary)
}
}
if let name = reaction["name"] as? String {
reactions[name] = Reaction(name: name, users: userDictionary)
}
}
return reactions
@@ -108,7 +109,7 @@ public struct Comment {
internal(set) public var stars: Int?
internal(set) public var reactions = [String: Reaction]()
internal init?(comment:[String: AnyObject]?) {
internal init?(comment:[String: Any]?) {
id = comment?["id"] as? String
created = comment?["created"] as? Int
user = comment?["user"] as? String
@@ -139,23 +140,23 @@ public struct Item {
public let comment: Comment?
public let fileCommentID: String?
internal init?(item:[String: AnyObject]?) {
internal init?(item:[String: Any]?) {
type = item?["type"] as? String
ts = item?["ts"] as? String
channel = item?["channel"] as? String
message = Message(message: item?["message"] as? [String: AnyObject])
message = Message(message: item?["message"] as? [String: Any])
// Comment and File can come across as Strings or Dictionaries
if (Comment(comment: item?["comment"] as? [String: AnyObject])?.id == nil) {
if (Comment(comment: item?["comment"] as? [String: Any])?.id == nil) {
comment = Comment(id: item?["comment"] as? String)
} else {
comment = Comment(comment: item?["comment"] as? [String: AnyObject])
comment = Comment(comment: item?["comment"] as? [String: Any])
}
if (File(file: item?["file"] as? [String: AnyObject])?.id == nil) {
if (File(file: item?["file"] as? [String: Any])?.id == nil) {
file = File(id: item?["file"] as? String)
} else {
file = File(file: item?["file"] as? [String: AnyObject])
file = File(file: item?["file"] as? [String: Any])
}
fileCommentID = item?["file_comment"] as? String
@@ -174,7 +175,7 @@ public struct Topic {
public let creator: String?
public let lastSet: Int?
internal init?(topic: [String: AnyObject]?) {
internal init?(topic: [String: Any]?) {
value = topic?["value"] as? String
creator = topic?["creator"] as? String
lastSet = topic?["last_set"] as? Int
@@ -189,7 +190,7 @@ public struct DoNotDisturbStatus {
internal(set) public var snoozeEnabled: Bool?
internal(set) public var snoozeEndtime: Int?
internal init?(status: [String: AnyObject]?) {
internal init?(status: [String: Any]?) {
enabled = status?["dnd_enabled"] as? Bool
nextDoNotDisturbStart = status?["next_dnd_start_ts"] as? Int
nextDoNotDisturbEnd = status?["next_dnd_end_ts"] as? Int
@@ -203,10 +204,10 @@ public struct DoNotDisturbStatus {
public struct CustomProfile {
internal(set) public var fields = [String: CustomProfileField]()
internal init?(profile: [String: AnyObject]?) {
if let eventFields = profile?["fields"] as? [AnyObject] {
internal init?(profile: [String: Any]?) {
if let eventFields = profile?["fields"] as? [Any] {
for field in eventFields {
if let cpf = CustomProfileField(field: field as? [String: AnyObject]), id = cpf.id {
if let cpf = CustomProfileField(field: field as? [String: Any]), id = cpf.id {
fields[id] = cpf
} else {
if let cpf = CustomProfileField(id: field as? String), id = cpf.id {
@@ -217,10 +218,10 @@ public struct CustomProfile {
}
}
internal init?(customFields: [String: AnyObject]?) {
internal init?(customFields: [String: Any]?) {
if let customFields = customFields {
for key in customFields.keys {
if let cpf = CustomProfileField(field: customFields[key] as? [String: AnyObject]) {
if let cpf = CustomProfileField(field: customFields[key] as? [String: Any]) {
self.fields[key] = cpf
}
}
@@ -241,7 +242,7 @@ public struct CustomProfileField {
internal(set) public var possibleValues: [String]?
internal(set) public var type: String?
internal init?(field: [String: AnyObject]?) {
internal init?(field: [String: Any]?) {
id = field?["id"] as? String
alt = field?["alt"] as? String
value = field?["value"] as? String
+6 -6
View File
@@ -37,7 +37,7 @@ public struct User {
internal(set) public var image192: String?
internal(set) public var customProfile: CustomProfile?
internal init?(profile: [String: AnyObject]?) {
internal init?(profile: [String: Any]?) {
firstName = profile?["first_name"] as? String
lastName = profile?["last_name"] as? String
realName = profile?["real_name"] as? String
@@ -49,7 +49,7 @@ public struct User {
image48 = profile?["image_48"] as? String
image72 = profile?["image_72"] as? String
image192 = profile?["image_192"] as? String
customProfile = CustomProfile(customFields: profile?["fields"] as? [String: AnyObject])
customProfile = CustomProfile(customFields: profile?["fields"] as? [String: Any])
}
}
@@ -73,15 +73,15 @@ public struct User {
internal(set) public var timeZone: String?
internal(set) public var timeZoneLabel: String?
internal(set) public var timeZoneOffSet: Int?
internal(set) public var preferences: [String: AnyObject]?
internal(set) public var preferences: [String: Any]?
// Client properties
internal(set) public var userGroups: [String: String]?
internal init?(user: [String: AnyObject]?) {
internal init?(user: [String: Any]?) {
id = user?["id"] as? String
name = user?["name"] as? String
deleted = user?["deleted"] as? Bool
profile = Profile(profile: user?["profile"] as? [String: AnyObject])
profile = Profile(profile: user?["profile"] as? [String: Any])
color = user?["color"] as? String
isAdmin = user?["is_admin"] as? Bool
isOwner = user?["is_owner"] as? Bool
@@ -96,7 +96,7 @@ public struct User {
timeZone = user?["tz"] as? String
timeZoneLabel = user?["tz_label"] as? String
timeZoneOffSet = user?["tz_offset"] as? Int
preferences = user?["prefs"] as? [String: AnyObject]
preferences = user?["prefs"] as? [String: Any]
}
internal init?(id: String?) {
+3 -3
View File
@@ -39,11 +39,11 @@ public struct UserGroup {
public let createdBy: String?
internal(set) public var updatedBy: String?
internal(set) public var deletedBy: String?
internal(set) public var preferences: [String: AnyObject]?
internal(set) public var preferences: [String: Any]?
internal(set) public var users: [String]?
internal(set) public var userCount: Int?
internal init?(userGroup: [String: AnyObject]?) {
internal init?(userGroup: [String: Any]?) {
id = userGroup?["id"] as? String
teamID = userGroup?["team_id"] as? String
isUserGroup = userGroup?["is_usergroup"] as? Bool
@@ -58,7 +58,7 @@ public struct UserGroup {
createdBy = userGroup?["created_by"] as? String
updatedBy = userGroup?["updated_by"] as? String
deletedBy = userGroup?["deleted_by"] as? String
preferences = userGroup?["prefs"] as? [String: AnyObject]
preferences = userGroup?["prefs"] as? [String: Any]
users = userGroup?["users"] as? [String]
if let count = userGroup?["user_count"] as? String {
userCount = Int(count)