Compare commits

...

72 Commits

Author SHA1 Message Date
Peter Zignego 4230d7841c Merge pull request #32 from pvzig/feature/carthage-support
Feature/carthage support
2016-05-15 16:22:18 -04:00
Peter Zignego 8995a54d15 Fix project file and schemes after merge 2016-05-15 16:16:26 -04:00
Peter Zignego ba43a123ea Merge branch 'master' into feature/carthage-support
# Conflicts:
#	SlackKit.podspec
#	SlackKit.xcodeproj/project.pbxproj
#	SlackKit.xcodeproj/xcshareddata/xcschemes/SlackKit.xcscheme
2016-05-15 15:57:27 -04:00
Peter Zignego d00419dc18 Framework build details 2016-05-15 15:41:42 -04:00
Peter Zignego 15f3a30ba9 Framework build details 2016-05-15 15:39:47 -04:00
Peter Zignego befa53e3a9 Update ignore 2016-05-15 15:28:43 -04:00
Peter Zignego 35dfa840e8 Revert "Use submodule to handle core dependency"
This reverts commit edf56e1678.
2016-05-15 15:25:36 -04:00
Peter Zignego edf56e1678 Use submodule to handle core dependency 2016-05-15 15:21:47 -04:00
Peter Zignego 3eab323564 Ignore Cartfile.resolved 2016-05-15 15:04:34 -04:00
Peter Zignego 0b2ee1d10e Clean up project file 2016-05-15 14:51:15 -04:00
Peter Zignego 13335b5de1 Add additional shared schemes 2016-05-15 14:09:21 -04:00
Peter Zignego c8d8e18db6 Move AttachmentColor enum 2016-05-14 20:11:14 -04:00
Peter Zignego c11321c116 File name comment fixes 2016-05-14 20:08:54 -04:00
Michal Laskowski 0041a118a7 Fix circular dependencies (#30)
* Remove EventDispatcher, EventHandler classes; extend Client

* Make delegates weak

* Remove unneded 'if let delegate = '

* Move files around
2016-05-14 19:51:08 -04:00
Michal Laskowski 6cb5280cf5 Add tvOS target to podspec 2016-05-11 10:12:15 -05:00
Peter Zignego 9cad043957 Add shared scheme 2016-05-06 15:02:39 -04:00
Peter Zignego fa15ed8751 Merge pull request #28 from hamin/add-larger-thumbs-to-file
Adding support for larger size File thumbs
2016-05-04 12:02:42 -04:00
Haris Amin 0c9c146d58 Adding support for larger size File thumbs 2016-05-04 01:58:43 -04:00
Peter Zignego 109a20fac2 Readme update 2016-04-27 23:18:19 -04:00
Peter Zignego a6cf1e3cb6 Revert "Delete and add Cartfile.resolved to ignore"
This reverts commit d3c16bed5f.
2016-04-27 23:17:41 -04:00
Peter Zignego d3c16bed5f Delete and add Cartfile.resolved to ignore 2016-04-27 23:07:55 -04:00
Peter Zignego 797d95ab75 Update readme 2016-04-27 22:54:30 -04:00
Nate Stedman efb7bc9458 Use Carthage for resolving dependencies. 2016-04-27 22:34:28 -04:00
Peter Zignego 79e59f23fb Bump version number 2016-04-27 19:33:48 -04:00
Peter Zignego 01e199659e Update readme 2016-04-27 19:30:48 -04:00
Peter Zignego 851b2f5e14 Merge pull request #27 from pvzig/1.0.2
v1.0.2
2016-04-27 19:26:26 -04:00
Peter Zignego c506512800 Add files.info 2016-04-27 19:18:12 -04:00
Peter Zignego 20fa05605b Merge branch 'master' of https://github.com/pvzig/SlackRTMKit 2016-04-27 17:44:55 -04:00
Peter Zignego 30baf1f76c Add clientConnectionFailed delegate 2016-04-27 17:44:51 -04:00
Peter Zignego 1c3f01f861 Fix typo in example 2016-04-27 17:39:23 -04:00
Peter Zignego 47dc9b3d9f Merge pull request #25 from muratayusuke/feature/make_token_public
Make token public
2016-04-25 10:28:45 -04:00
muratayusuke b9e828ef3d Make token public 2016-04-24 18:44:40 +09: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
Peter Zignego d2037f4cc5 Merge pull request #23 from pvzig/feature/client-improvements
Feature/client-improvements
2016-03-22 21:44:20 -04:00
Peter Zignego b87232dfb5 Readme updates 2016-03-22 21:38:17 -04:00
Peter Zignego 9e5678739f Update readme 2016-03-22 21:34:45 -04:00
Peter Zignego 5cc8582d65 Code cleanup 2016-03-22 21:29:16 -04:00
Peter Zignego aa078934c0 File comment web api errors 2016-03-22 20:56:10 -04:00
Peter Zignego 5c157caea3 Client improvements - disconnect, ping-pong, timeout, reconnect 2016-03-22 20:50:29 -04:00
Peter Zignego b567113b5f Update Starscream dependency version 2016-03-22 20:44:58 -04:00
Peter Zignego 5b08fb6031 Merge pull request #21 from pvzig/feature/file-comment
Add support for file comment web API calls
2016-03-19 14:21:52 -04:00
Peter Zignego b48f33fb72 Add support for file comment web API calls 2016-03-19 14:12:04 -04:00
Peter Zignego deccb727a1 Merge pull request #20 from pvzig/feature/reactions-itemUser
Add support for the item user reaction property
2016-03-15 23:48:55 -04:00
Peter Zignego 8f1df8d138 Add support for the itemUser reaction property 2016-03-15 23:48:08 -04:00
Peter Zignego 0048710e24 Merge pull request #19 from pvzig/feature/dnd-read
DND Read Scope Web API implementation
2016-03-15 23:38:08 -04:00
Peter Zignego f6da0ddd32 DND read scope fixes 2016-03-15 23:37:33 -04:00
Peter Zignego 513485e704 DND Read Scope Web API implementation 2016-03-15 23:28:38 -04:00
Peter Zignego fb3719c29d Merge pull request #18 from pvzig/feature/rtm-team-profile
Add support for team profile events
2016-03-15 22:48:43 -04:00
Peter Zignego 76fdc55f9e Team profile events 2016-03-15 22:45:45 -04:00
Peter Zignego 687b57fc1f Version bump 2016-03-01 23:08:24 -05:00
Peter Zignego 319dc9a095 Merge pull request #17 from muratayusuke/bugfix/ignore_int_param
Bugfix: Do not ignore Int and Bool parameters when making requests to the web api
2016-03-01 12:47:20 -05:00
muratayusuke 64440b5c5b Do not ignore Int/Bool param such as count 2016-03-02 02:25:37 +09:00
Peter Zignego 3efe5752cb Merge pull request #16 from pvzig/feature/history-object
Feature/history object
2016-02-28 12:41:22 -05:00
Peter Zignego 31f69e3afd Move History struct to Types.swift, minor changes 2016-02-28 12:40:04 -05:00
muratayusuke 72b29562e5 Introduce history type 2016-02-28 12:29:12 -05:00
Peter Zignego 2b26eb35d7 Merge pull request #14 from pvzig/fix/team-object
fix/team-object
2016-02-27 14:13:53 -05:00
Peter Zignego c4c8e99c0b Bug fixes, team icon support 2016-02-27 14:10:35 -05:00
muratayusuke 5efe222196 Add team icon 2016-02-27 14:06:24 -05:00
Peter Zignego e56ff971b2 Merge pull request #12 from hamin/public-message-initializer
Making Message struct initializer public.
2016-02-27 13:48:42 -05:00
Haris Amin 18b8a31d85 Making Message struct initializer public. 2016-02-27 13:38:52 -05:00
Peter Zignego d386730c75 Project file update 2016-02-21 10:31:13 -05:00
Peter Zignego bc6e94d99a Merge pull request #11 from pvzig/feature/message-attachments
Message attachment support
2016-02-21 10:27:41 -05:00
Peter Zignego 89f5e3786e Message attachment support 2016-02-21 10:18:32 -05:00
Peter Zignego 0665ea0270 Add sample app 2016-02-18 18:21:17 -05:00
38 changed files with 3140 additions and 955 deletions
+5 -3
View File
@@ -16,8 +16,10 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
.build
# SwiftPM
Packages/
.build
# CocoaPods
#
@@ -26,10 +28,10 @@ Packages/
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
Pods/
SlackKit.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Checkouts
Carthage/Build
+1
View File
@@ -0,0 +1 @@
github "daltoniam/Starscream" ~> 1.0
+1
View File
@@ -0,0 +1 @@
github "daltoniam/Starscream" "1.1.3"
+20 -6
View File
@@ -1,5 +1,6 @@
//
// SlackKit.h
// AppDelegate.swift
// OSX-Sample
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -21,10 +22,23 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
import Cocoa
import SlackKit
//! Project version number for SlackKit.
FOUNDATION_EXPORT double SlackKitVersionNumber;
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
let leaderboard = Leaderboard(token: "SLACK_AUTH_TOKEN")
func applicationDidFinishLaunching(aNotification: NSNotification) {
leaderboard.client.connect()
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
//! Project version string for SlackKit.
FOUNDATION_EXPORT const unsigned char SlackKitVersionString[];
@@ -0,0 +1,58 @@
{
"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
@@ -0,0 +1,680 @@
<?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
@@ -0,0 +1,34 @@
<?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>
+160
View File
@@ -0,0 +1,160 @@
//
// Leaderboard.swift
// OSX-Sample
//
// 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
import Foundation
class Leaderboard: MessageEventsDelegate {
var leaderboard: [String: Int] = [String: Int]()
let atSet = NSCharacterSet(charactersInString: "@")
let client: Client
init(token: String) {
client = Client(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)
}
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.lowercaseString.containsString(Command.Leaderboard.rawValue) && text.containsString(id) == true {
handleCommand(.Leaderboard, channel: message.channel)
}
}
if message.text?.containsString(Trigger.PlusPlus.rawValue) == true {
handleMessageWithTrigger(message, trigger: .PlusPlus)
}
if message.text?.containsString(Trigger.MinusMinus.rawValue) == true {
handleMessageWithTrigger(message, trigger: .MinusMinus)
}
}
private func handleMessageWithTrigger(message: Message, trigger: Trigger) {
if let text = message.text,
end = text.rangeOfString(trigger.rawValue)?.startIndex.predecessor(),
start = text.rangeOfCharacterFromSet(atSet, options: .BackwardsSearch, range: text.startIndex..<end)?.startIndex {
let string = text.substringWithRange(start...end)
let users = client.users.values.filter{$0.id == self.userID(string)}
if users.count > 0 {
let idString = userID(string)
initalizationForValue(&leaderboard, value: idString)
scoringForValue(&leaderboard, value: idString, trigger: trigger)
} else {
initalizationForValue(&leaderboard, value: string)
scoringForValue(&leaderboard, value: string, trigger: trigger)
}
}
}
private func handleCommand(command: Command, channel:String?) {
switch command {
case .Leaderboard:
if let id = channel {
client.webAPI.sendMessage(id, text: "", linkNames: true, attachments: [constructLeaderboardAttachment()], success: {(response) in
}, failure: { (error) in
print("Leaderboard failed to post due to error:\(error)")
})
}
}
}
private func initalizationForValue(inout dictionary: [String: Int], value: String) {
if dictionary[value] == nil {
dictionary[value] = 0
}
}
private func scoringForValue(inout dictionary: [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(topItems(&leaderboard)), short: true)
let 💩 = AttachmentField(title: "💩", value: swapIDsForNames(bottomItems(&leaderboard)), short: true)
return Attachment(fallback: "Leaderboard", title: "Leaderboard", colorHex: AttachmentColor.Good.rawValue, text: "", fields: [💯, 💩])
}
private func topItems(inout dictionary: [String: Int]) -> String {
let sortedKeys = Array(dictionary.keys).sort({dictionary[$0] > dictionary[$1]}).filter({dictionary[$0] > 0})
let sortedValues = Array(dictionary.values).sort({$0 > $1}).filter({$0 > 0})
return leaderboardString(sortedKeys, values: sortedValues)
}
private func bottomItems(inout dictionary: [String: Int]) -> String {
let sortedKeys = Array(dictionary.keys).sort({dictionary[$0] < dictionary[$1]}).filter({dictionary[$0] < 0})
let sortedValues = Array(dictionary.values).sort({$0 < $1}).filter({$0 < 0})
return leaderboardString(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 {
returnString = returnString.stringByReplacingOccurrencesOfString(key, withString: "@"+name, options: NSStringCompareOptions.LiteralSearch, range: returnString.startIndex..<returnString.endIndex)
}
}
return returnString
}
private func userID(string: String) -> String {
return string.stringByTrimmingCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet)
}
}
+8
View File
@@ -4,4 +4,12 @@ use_frameworks!
target 'SlackKit' do
pod 'Starscream'
end
target 'SlackKit_iOS' do
pod 'Starscream'
end
target 'SlackKit_tvOS' do
pod 'Starscream'
end
+5 -3
View File
@@ -1,10 +1,12 @@
PODS:
- Starscream (1.0.2)
- Starscream (1.1.3)
DEPENDENCIES:
- Starscream
SPEC CHECKSUMS:
Starscream: 40e2c4c1c770d811f24116b8b7dbeb4542c56767
Starscream: d662732354b40dd19ed1ece3e3c44c80b536b83c
COCOAPODS: 0.39.0
PODFILE CHECKSUM: d22778f772dbbded8b17fbebf5fa1c879d785aee
COCOAPODS: 1.0.0
+55 -19
View File
@@ -1,24 +1,12 @@
![SlackKit](https://cloud.githubusercontent.com/assets/8311605/10260893/5ec60f96-694e-11e5-91fd-da6845942201.png)
##iOS/OS X 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 OS X, iOS, and tvOS written in Swift. It's intended to expose all of the functionality of Slack's [Real Time Messaging API](https://api.slack.com/rtm) as well as the [web APIs](https://api.slack.com/web) that are accessible by [bot users](https://api.slack.com/bot-users).
####Building the SlackKit Framework
To build the SlackKit project directly, first build the dependencies using Carthage or CocoaPods. To use the framework in your application, install it in one of the following ways:
###Installation
####Swift Package Manager
Add SlackKit to your Package.swift
```swift
import PackageDescription
let package = Package(
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 0)
]
)
```
Run `swift-build` on your applications main directory.
####CocoaPods
Add the pod to your podfile:
```
@@ -29,6 +17,33 @@ and run
pod install
```
####Carthage
Add SlackKit to your Cartfile:
```
github “pvzig/SlackKit” ~> 1.0
```
and run
```
carthage update
```
Drag the built SlackKit.framework into your Xcode project.
####Swift Package Manager
Add SlackKit to your Package.swift
```swift
import PackageDescription
let package = Package(
dependencies: [
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 1)
]
)
```
Run `swift-build` on your applications main directory.
To use the library in your project import it:
```
import SlackKit
@@ -40,7 +55,6 @@ To use SlackKit you'll need a bearer token which identifies a single user. You c
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,6 +62,11 @@ 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
@@ -65,7 +84,11 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- chat.postMessage
- chat.update
- emoji.list
- files.comments.add
- files.comments.edit
- files.comments.delete
- files.delete
- files.info
- files.upload
- groups.close
- groups.history
@@ -123,6 +146,7 @@ There are a number of delegates that you can set to receive callbacks for certai
#####SlackEventsDelegate
```swift
func clientConnectionFailed(error: SlackError)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
@@ -187,8 +211,8 @@ func itemStarred(item: Item, star: Bool)
#####ReactionEventsDelegate
```swift
func reactionAdded(reaction: String?, item: Item?)
func reactionRemoved(reaction: String?, item: Item?)
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
```
#####TeamEventsDelegate
@@ -209,6 +233,18 @@ func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
```
###Examples
####Leaderboard
Included in the OSX-Sample is an example application of a bot you might make using SlackKit. Its a basic leaderboard scoring bot, in the spirit of [PlusPlus](https://plusplus.chat).
To configure it, enter your bots API token in `AppDelegate.swift` for the Leaderboard bot:
```swift
let learderboard = Leaderboard(token: "SLACK_AUTH_TOKEN")
```
It adds a point for every `@thing++`, subtracts a point for every `@thing--`, and shows a leaderboard when asked `@botname leaderboard`.
###Get In Touch
[@pvzig](https://twitter.com/pvzig)
+5 -3
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "0.9.8"
s.version = "1.0.3"
s.summary = "a Slack client library for iOS and OS X written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
@@ -8,9 +8,11 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/pvzig/SlackKit.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/pvzig'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0'
s.requires_arc = true
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.0.2'
s.dependency 'Starscream', '~> 1.1.3'
end
+478 -66
View File
@@ -8,78 +8,163 @@
/* Begin PBXBuildFile section */
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
260EC2331C4DC61D0093B253 /* ClientExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* ClientExtensions.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 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993901CE90C87004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993981CE90EE0004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993991CE90EE0004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
2639939A1CE90EE0004A6E93 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.swift */; };
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
2639939F1CE90EE0004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993A01CE90EE0004A6E93 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993A21CE90EE0004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993A31CE90EE0004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993B71CE90EED004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993B81CE90EED004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
263993B91CE90EED004A6E93 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.swift */; };
263993BA1CE90EED004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
263993BB1CE90EED004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
263993BE1CE90EED004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993BF1CE90EED004A6E93 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993C11CE90EED004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993C21CE90EED004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */; };
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
26F4BAC31C9DEBD1000910BA /* Leaderboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */; };
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2601D61A1C7646B80012BF22 /* 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>"; };
260EC2301C4DC61D0093B253 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = "<group>"; };
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkInterface.swift; path = Sources/NetworkInterface.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPI.swift; path = Sources/SlackWebAPI.swift; sourceTree = "<group>"; };
263993B21CE90EE0004A6E93 /* SlackKit_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
263993D11CE90EED004A6E93 /* SlackKit_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
268E46131CE8F79D009F19CC /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-iOS.plist"; path = "Supporting Files/Info-iOS.plist"; sourceTree = "<group>"; };
268E46141CE8F79D009F19CC /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-tvOS.plist"; path = "Supporting Files/Info-tvOS.plist"; sourceTree = "<group>"; };
268E46151CE8F79D009F19CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Supporting Files/Info.plist"; sourceTree = "<group>"; };
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>"; };
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>"; };
26DF40341C7A0FA300E19241 /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Attachment.swift; path = Sources/Attachment.swift; sourceTree = "<group>"; };
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Leaderboard.swift; sourceTree = "<group>"; };
4307A07F1CC6D0910011D5DE /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/Mac/Starscream.framework; sourceTree = "<group>"; };
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+Utilities.swift"; path = "Sources/Client+Utilities.swift"; sourceTree = "<group>"; };
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventDispatching.swift"; path = "Sources/Client+EventDispatching.swift"; sourceTree = "<group>"; };
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventHandling.swift"; path = "Sources/Client+EventHandling.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2601D6211C7688610012BF22 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A301BB48B3A00CD650C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */,
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AA1CE90EE0004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993C91CE90EED004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0C2928E508A686B69F9F0117 /* Pods */ = {
2601D6251C7688610012BF22 /* OSX-Sample */ = {
isa = PBXGroup;
children = (
F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */,
4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */,
2601D6261C7688610012BF22 /* AppDelegate.swift */,
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */,
2601D6281C7688610012BF22 /* Assets.xcassets */,
2601D62A1C7688610012BF22 /* MainMenu.xib */,
2601D62D1C7688610012BF22 /* Info.plist */,
);
name = Pods;
path = "OSX-Sample";
sourceTree = "<group>";
};
26072A2A1BB48B3A00CD650C = {
isa = PBXGroup;
children = (
2661A6811BBF60E60026F67B /* SlackKit */,
2601D6251C7688610012BF22 /* OSX-Sample */,
26072A351BB48B3A00CD650C /* Products */,
0C2928E508A686B69F9F0117 /* Pods */,
CA70A3A1A9A1A259960DFBCF /* Frameworks */,
);
sourceTree = "<group>";
@@ -88,6 +173,9 @@
isa = PBXGroup;
children = (
26072A341BB48B3A00CD650C /* SlackKit.framework */,
2601D6241C7688610012BF22 /* OSX-Sample.app */,
263993B21CE90EE0004A6E93 /* SlackKit_iOS.framework */,
263993D11CE90EED004A6E93 /* SlackKit_tvOS.framework */,
);
name = Products;
sourceTree = "<group>";
@@ -95,14 +183,16 @@
2661A6811BBF60E60026F67B /* SlackKit */ = {
isa = PBXGroup;
children = (
26DF40341C7A0FA300E19241 /* Attachment.swift */,
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
26BBA1891C398E3C00BF7225 /* Client.swift */,
260EC2301C4DC61D0093B253 /* ClientExtensions.swift */,
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */,
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */,
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */,
26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */,
26BBA18D1C398E3C00BF7225 /* EventHandler.swift */,
260EC2301C4DC61D0093B253 /* Extensions.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */,
@@ -112,16 +202,26 @@
26BBA1911C398E3C00BF7225 /* Types.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
266E05F01BBF780C00840D76 /* Info.plist */,
268E46161CE8F7A2009F19CC /* Supporting Files */,
);
path = SlackKit;
sourceTree = "<group>";
};
268E46161CE8F7A2009F19CC /* Supporting Files */ = {
isa = PBXGroup;
children = (
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
268E46151CE8F79D009F19CC /* Info.plist */,
268E46131CE8F79D009F19CC /* Info-iOS.plist */,
268E46141CE8F79D009F19CC /* Info-tvOS.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CA70A3A1A9A1A259960DFBCF /* Frameworks */ = {
isa = PBXGroup;
children = (
407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */,
4307A07F1CC6D0910011D5DE /* Starscream.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -133,22 +233,54 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993901CE90C87004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AC1CE90EE0004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CB1CE90EED004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
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 = (
);
@@ -159,16 +291,55 @@
productReference = 26072A341BB48B3A00CD650C /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
263993951CE90EE0004A6E93 /* SlackKit_iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit_iOS" */;
buildPhases = (
263993961CE90EE0004A6E93 /* Sources */,
263993AA1CE90EE0004A6E93 /* Frameworks */,
263993AC1CE90EE0004A6E93 /* Headers */,
263993AE1CE90EE0004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SlackKit_iOS;
productName = SlackRTMKit;
productReference = 263993B21CE90EE0004A6E93 /* SlackKit_iOS.framework */;
productType = "com.apple.product-type.framework";
};
263993B41CE90EED004A6E93 /* SlackKit_tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit_tvOS" */;
buildPhases = (
263993B51CE90EED004A6E93 /* Sources */,
263993C91CE90EED004A6E93 /* Frameworks */,
263993CB1CE90EED004A6E93 /* Headers */,
263993CD1CE90EED004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SlackKit_tvOS;
productName = SlackRTMKit;
productReference = 263993D11CE90EED004A6E93 /* SlackKit_tvOS.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
26072A2B1BB48B3A00CD650C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "Launch Software LLC";
TargetAttributes = {
2601D6231C7688610012BF22 = {
CreatedOnToolsVersion = 7.2.1;
};
26072A331BB48B3A00CD650C = {
CreatedOnToolsVersion = 7.0;
};
@@ -180,6 +351,7 @@
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 26072A2A1BB48B3A00CD650C;
productRefGroup = 26072A351BB48B3A00CD650C /* Products */;
@@ -187,11 +359,23 @@
projectRoot = "";
targets = (
26072A331BB48B3A00CD650C /* SlackKit */,
263993951CE90EE0004A6E93 /* SlackKit_iOS */,
263993B41CE90EED004A6E93 /* SlackKit_tvOS */,
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;
@@ -199,69 +383,150 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AE1CE90EE0004A6E93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CD1CE90EED004A6E93 /* 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 */,
26F4BAC31C9DEBD1000910BA /* Leaderboard.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A2F1BB48B3A00CD650C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26BBA1991C398E3C00BF7225 /* EventDispatcher.swift in Sources */,
C16C987A1CE7D3DD00692776 /* Client+Utilities.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 */,
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.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 */,
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */,
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */,
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */,
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */,
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993961CE90EE0004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */,
263993981CE90EE0004A6E93 /* Channel.swift in Sources */,
263993991CE90EE0004A6E93 /* User.swift in Sources */,
2639939A1CE90EE0004A6E93 /* Types.swift in Sources */,
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */,
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */,
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */,
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */,
2639939F1CE90EE0004A6E93 /* File.swift in Sources */,
263993A01CE90EE0004A6E93 /* SlackWebAPI.swift in Sources */,
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */,
263993A21CE90EE0004A6E93 /* Message.swift in Sources */,
263993A31CE90EE0004A6E93 /* Team.swift in Sources */,
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */,
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */,
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */,
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */,
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993B51CE90EED004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */,
263993B71CE90EED004A6E93 /* Channel.swift in Sources */,
263993B81CE90EED004A6E93 /* User.swift in Sources */,
263993B91CE90EED004A6E93 /* Types.swift in Sources */,
263993BA1CE90EED004A6E93 /* Client.swift in Sources */,
263993BB1CE90EED004A6E93 /* Event.swift in Sources */,
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */,
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */,
263993BE1CE90EED004A6E93 /* File.swift in Sources */,
263993BF1CE90EED004A6E93 /* SlackWebAPI.swift in Sources */,
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */,
263993C11CE90EED004A6E93 /* Message.swift in Sources */,
263993C21CE90EED004A6E93 /* Team.swift in Sources */,
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */,
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */,
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */,
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */,
263993C81CE90EED004A6E93 /* 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 = {
@@ -298,6 +563,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -338,6 +604,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
@@ -348,7 +615,6 @@
};
26072A3D1BB48B3B00CD650C /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
@@ -356,10 +622,15 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
@@ -369,7 +640,6 @@
};
26072A3E1BB48B3B00CD650C /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
@@ -377,19 +647,143 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
};
name = Release;
};
263993B01CE90EE0004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
263993B11CE90EE0004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
};
name = Release;
};
263993CF1CE90EED004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
263993D01CE90EED004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
TVOS_DEPLOYMENT_TARGET = 9.0;
};
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 = (
@@ -408,6 +802,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit_iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993B01CE90EE0004A6E93 /* Debug */,
263993B11CE90EE0004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit_tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993CF1CE90EED004A6E93 /* Debug */,
263993D01CE90EED004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 26072A2B1BB48B3A00CD650C /* Project object */;
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit_iOS.framework"
BlueprintName = "SlackKit_iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit_iOS.framework"
BlueprintName = "SlackKit_iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit_iOS.framework"
BlueprintName = "SlackKit_iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit_tvOS.framework"
BlueprintName = "SlackKit_tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit_tvOS.framework"
BlueprintName = "SlackKit_tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit_tvOS.framework"
BlueprintName = "SlackKit_tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-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>
+134
View File
@@ -0,0 +1,134 @@
//
// Attachment.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct Attachment {
public let fallback: String?
public let color: String?
public let pretext: String?
public let authorName: String?
public let authorLink: String?
public let authorIcon: String?
public let title: String?
public let titleLink: String?
public let text: String?
public let fields: [AttachmentField]?
public let imageURL: String?
public let thumbURL: String?
internal init?(attachment: [String: AnyObject]?) {
fallback = attachment?["fallback"] as? String
color = attachment?["color"] as? String
pretext = attachment?["pretext"] as? String
authorName = attachment?["author_name"] as? String
authorLink = attachment?["author_link"] as? String
authorIcon = attachment?["author_icon"] as? String
title = attachment?["title"] as? String
titleLink = attachment?["title_link"] as? String
text = attachment?["text"] as? String
imageURL = attachment?["image_url"] as? String
thumbURL = attachment?["thumb_url"] as? String
fields = (attachment?["fields"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(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: [AttachmentField]? = nil, imageURL: String? = nil, thumbURL: String? = nil) {
self.fallback = fallback
self.color = colorHex
self.pretext = pretext
self.authorName = authorName
self.authorLink = authorLink
self.authorIcon = authorIcon
self.title = title
self.titleLink = titleLink
self.text = text
self.fields = fields
self.imageURL = imageURL
self.thumbURL = thumbURL
}
internal func dictionary() -> [String: AnyObject] {
var attachment = [String: AnyObject]()
attachment["fallback"] = fallback
attachment["color"] = color
attachment["pretext"] = pretext
attachment["authorName"] = authorName
attachment["author_link"] = authorLink
attachment["author_icon"] = authorIcon
attachment["title"] = title
attachment["title_link"] = titleLink
attachment["text"] = text
attachment["fields"] = fieldJSONArray(fields)
attachment["image_url"] = imageURL
attachment["thumb_url"] = thumbURL
return attachment
}
private func fieldJSONArray(fields: [AttachmentField]?) -> [[String: AnyObject]] {
var returnValue = [[String: AnyObject]]()
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: AnyObject]?) {
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: AnyObject] {
var field = [String: AnyObject]()
field["title"] = title
field["value"] = value
field["short"] = short
return field
}
}
public enum AttachmentColor: String {
case Good = "good"
case Warning = "warning"
case Danger = "danger"
}
+6 -5
View File
@@ -43,7 +43,7 @@ public struct Channel {
internal(set) public var unread: Int?
internal(set) public var unreadCountDisplay: Int?
internal(set) public var hasPins: Bool?
internal(set) public var members = [String]()
internal(set) public var members: [String]?
// Client use
internal(set) public var pinnedItems = [Item]()
internal(set) public var usersTyping = [String]()
@@ -66,15 +66,16 @@ public struct Channel {
purpose = Topic(topic: channel?["purpose"] as? [String: AnyObject])
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 let members = channel?["members"] as? [String] {
self.members = members
if (Message(message: channel?["latest"] as? [String: AnyObject])?.ts == nil) {
latest = Message(ts: channel?["latest"] as? String)
} else {
latest = Message(message: channel?["latest"] as? [String: AnyObject])
}
}
internal init?(id:String?) {
@@ -1,5 +1,5 @@
//
// EventDispatcher.swift
// Client+EventDispatching.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -21,129 +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
required init(client: Client) {
self.client = client
handler = EventHandler(client: client)
}
internal extension Client {
func dispatch(event: [String: AnyObject]) {
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)
case .Message:
if (event.subtype != nil) {
messageDispatcher(event)
} else {
handler.messageReceived(event)
messageReceived(event)
}
case .UserTyping:
handler.userTyping(event)
userTyping(event)
case .ChannelMarked, .IMMarked, .GroupMarked:
handler.channelMarked(event)
channelMarked(event)
case .ChannelCreated, .IMCreated:
handler.channelCreated(event)
channelCreated(event)
case .ChannelJoined, .GroupJoined:
handler.channelJoined(event)
channelJoined(event)
case .ChannelLeft, .GroupLeft:
handler.channelLeft(event)
channelLeft(event)
case .ChannelDeleted:
handler.channelDeleted(event)
channelDeleted(event)
case .ChannelRenamed, .GroupRename:
handler.channelRenamed(event)
channelRenamed(event)
case .ChannelArchive, .GroupArchive:
handler.channelArchived(event, archived: true)
channelArchived(event, archived: true)
case .ChannelUnarchive, .GroupUnarchive:
handler.channelArchived(event, archived: false)
channelArchived(event, archived: false)
case .ChannelHistoryChanged, .IMHistoryChanged, .GroupHistoryChanged:
handler.channelHistoryChanged(event)
channelHistoryChanged(event)
case .DNDUpdated:
handler.doNotDisturbUpdated(event)
doNotDisturbUpdated(event)
case .DNDUpatedUser:
handler.doNotDisturbUserUpdated(event)
doNotDisturbUserUpdated(event)
case .IMOpen, .GroupOpen:
handler.open(event, open: true)
open(event, open: true)
case .IMClose, .GroupClose:
handler.open(event, open: false)
open(event, open: false)
case .FileCreated:
handler.processFile(event)
processFile(event)
case .FileShared:
handler.processFile(event)
processFile(event)
case .FileUnshared:
handler.processFile(event)
processFile(event)
case .FilePublic:
handler.processFile(event)
processFile(event)
case .FilePrivate:
handler.filePrivate(event)
filePrivate(event)
case .FileChanged:
handler.processFile(event)
processFile(event)
case .FileDeleted:
handler.deleteFile(event)
deleteFile(event)
case .FileCommentAdded:
handler.fileCommentAdded(event)
fileCommentAdded(event)
case .FileCommentEdited:
handler.fileCommentEdited(event)
fileCommentEdited(event)
case .FileCommentDeleted:
handler.fileCommentDeleted(event)
fileCommentDeleted(event)
case .PinAdded:
handler.pinAdded(event)
pinAdded(event)
case .PinRemoved:
handler.pinRemoved(event)
pinRemoved(event)
case .Pong:
pong(event)
case .PresenceChange:
handler.presenceChange(event)
presenceChange(event)
case .ManualPresenceChange:
handler.manualPresenceChange(event)
manualPresenceChange(event)
case .PrefChange:
handler.changePreference(event)
changePreference(event)
case .UserChange:
handler.userChange(event)
userChange(event)
case .TeamJoin:
handler.teamJoin(event)
teamJoin(event)
case .StarAdded:
handler.itemStarred(event, star: true)
itemStarred(event, star: true)
case .StarRemoved:
handler.itemStarred(event, star: false)
itemStarred(event, star: false)
case .ReactionAdded:
handler.addedReaction(event)
addedReaction(event)
case .ReactionRemoved:
handler.removedReaction(event)
removedReaction(event)
case .EmojiChanged:
handler.emojiChanged(event)
emojiChanged(event)
case .CommandsChanged:
// Not implemented per Slack documentation.
// 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)
case .TeamPrefChange:
handler.teamPreferenceChange(event)
teamPreferenceChange(event)
case .TeamRename:
handler.teamNameChange(event)
teamNameChange(event)
case .TeamDomainChange:
handler.teamDomainChange(event)
teamDomainChange(event)
case .EmailDomainChange:
handler.emailDomainChange(event)
emailDomainChange(event)
case .TeamProfileChange:
teamProfileChange(event)
case .TeamProfileDelete:
teamProfileDeleted(event)
case .TeamProfileReorder:
teamProfileReordered(event)
case .BotAdded:
handler.bot(event)
bot(event)
case .BotChanged:
handler.bot(event)
bot(event)
case .AccountsChanged:
// Not implemented per Slack documentation.
// 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()
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)
case .SubteamSelfAdded:
handler.subteamAddedSelf(event)
subteamAddedSelf(event)
case.SubteamSelfRemoved:
handler.subteamRemovedSelf(event)
subteamRemovedSelf(event)
case .Error:
print("Error: \(event)")
break
@@ -155,11 +163,11 @@ internal class EventDispatcher {
let subtype = MessageSubtype(rawValue: event.subtype!)!
switch subtype {
case .MessageChanged:
handler.messageChanged(event)
messageChanged(event)
case .MessageDeleted:
handler.messageDeleted(event)
messageDeleted(event)
default:
handler.messageReceived(event)
messageReceived(event)
}
}
+550
View File
@@ -0,0 +1,550 @@
//
// 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 Foundation
internal extension Client {
//MARK: - Pong
func pong(event: Event) {
pong = event.replyTo
}
//MARK: - Messages
func messageSent(event: Event) {
if let reply = event.replyTo, message = sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts {
message.ts = event.ts
message.text = event.text
channels[channel]?.messages[ts] = message
messageEventsDelegate?.messageSent(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)
}
}
func messageChanged(event: Event) {
if let id = event.channel?.id, nested = event.nestedMessage, ts = nested.ts {
channels[id]?.messages[ts] = nested
messageEventsDelegate?.messageChanged(nested)
}
}
func messageDeleted(event: Event) {
if let id = event.channel?.id, key = event.message?.deletedTs {
let message = channels[id]?.messages[key]
channels[id]?.messages.removeValueForKey(key)
messageEventsDelegate?.messageDeleted(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(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.channels[channelID]?.usersTyping.indexOf(userID) {
self.channels[channelID]?.usersTyping.removeAtIndex(index)
}
}
}
}
func channelMarked(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id]?.lastRead = event.ts
channelEventsDelegate?.channelMarked(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)
}
}
func channelDeleted(event: Event) {
if let channel = event.channel, id = channel.id {
channels.removeValueForKey(id)
channelEventsDelegate?.channelDeleted(channel)
}
}
func channelJoined(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id] = event.channel
channelEventsDelegate?.channelJoined(channel)
}
}
func channelLeft(event: Event) {
if let channel = event.channel, id = channel.id, userID = authenticatedUser?.id {
if let index = channels[id]?.members?.indexOf(userID) {
channels[id]?.members?.removeAtIndex(index)
channelEventsDelegate?.channelLeft(channel)
}
}
}
func channelRenamed(event: Event) {
if let channel = event.channel, id = channel.id {
channels[id]?.name = channel.name
channelEventsDelegate?.channelRenamed(channel)
}
}
func channelArchived(event: Event, archived: Bool) {
if let channel = event.channel, id = channel.id {
channels[id]?.isArchived = archived
channelEventsDelegate?.channelArchived(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)
}
}
//MARK: - Do Not Disturb
func doNotDisturbUpdated(event: Event) {
if let dndStatus = event.dndStatus {
authenticatedUser?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUpdated(dndStatus)
}
}
func doNotDisturbUserUpdated(event: Event) {
if let dndStatus = event.dndStatus, user = event.user, id = user.id {
users[id]?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUserUpdated(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(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)
}
}
func filePrivate(event: Event) {
if let file = event.file, id = file.id {
files[id]?.isPublic = false
fileEventsDelegate?.fileMadePrivate(file)
}
}
func deleteFile(event: Event) {
if let file = event.file, id = file.id {
if files[id] != nil {
files.removeValueForKey(id)
}
fileEventsDelegate?.fileDeleted(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, 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, comment: comment)
}
}
func fileCommentDeleted(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
files[id]?.comments.removeValueForKey(commentID)
fileEventsDelegate?.fileCommentDeleted(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, 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(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, star: star)
case "file":
starFile(item, star: star)
case "file_comment":
starComment(item)
default:
break
}
starEventsDelegate?.itemStarred(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(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.removeValueForKey(userID)
}
if (message.reactions[key]?.users.count == 0) {
message.reactions.removeValueForKey(key)
}
}
}
case "file":
if let itemFile = item.file, id = itemFile.id, file = files[id] {
if file.reactions[key] != nil {
files[id]?.reactions[key]?.users.removeValueForKey(userID)
}
if files[id]?.reactions[key]?.users.count == 0 {
files[id]?.reactions.removeValueForKey(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.removeValueForKey(userID)
}
if files[id]?.comments[commentID]?.reactions[key]?.users.count == 0 {
files[id]?.comments[commentID]?.reactions.removeValueForKey(key)
}
}
break
default:
break
}
reactionEventsDelegate?.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
//MARK: - Preferences
func changePreference(event: Event) {
if let name = event.name {
authenticatedUser?.preferences?[name] = event.value
if let value = event.value {
slackEventsDelegate?.preferenceChanged(name, value: 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)
}
}
//MARK: - User Presence
func presenceChange(event: Event) {
if let user = event.user, id = user.id {
users[id]?.presence = event.presence
slackEventsDelegate?.presenceChanged(user, presence: event.presence)
}
}
//MARK: - Team
func teamJoin(event: Event) {
if let user = event.user, id = user.id {
users[id] = user
teamEventsDelegate?.teamJoined(user)
}
}
func teamPlanChange(event: Event) {
if let plan = event.plan {
team?.plan = plan
teamEventsDelegate?.teamPlanChanged(plan)
}
}
func teamPreferenceChange(event: Event) {
if let name = event.name {
team?.prefs?[name] = event.value
if let value = event.value {
teamEventsDelegate?.teamPreferencesChanged(name, value: value)
}
}
}
func teamNameChange(event: Event) {
if let name = event.name {
team?.name = name
teamEventsDelegate?.teamNameChanged(name)
}
}
func teamDomainChange(event: Event) {
if let domain = event.domain {
team?.domain = domain
teamEventsDelegate?.teamDomainChanged(domain)
}
}
func emailDomainChange(event: Event) {
if let domain = event.emailDomain {
team?.emailDomain = domain
teamEventsDelegate?.teamEmailDomainChanged(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)
}
}
//MARK: - Subteams
func subteam(event: Event) {
if let subteam = event.subteam, id = subteam.id {
userGroups[id] = subteam
subteamEventsDelegate?.subteamEvent(subteam)
}
}
func subteamAddedSelf(event: Event) {
if let subteamID = event.subteamID, _ = authenticatedUser?.userGroups {
authenticatedUser?.userGroups![subteamID] = subteamID
subteamEventsDelegate?.subteamSelfAdded(subteamID)
}
}
func subteamRemovedSelf(event: Event) {
if let subteamID = event.subteamID {
authenticatedUser?.userGroups?.removeValueForKey(subteamID)
subteamEventsDelegate?.subteamSelfRemoved(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(fields[key])
}
}
}
teamProfileEventsDelegate?.teamProfileChanged(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(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(event.profile)
}
//MARK: - Authenticated User
func manualPresenceChange(event: Event) {
authenticatedUser?.presence = event.presence
slackEventsDelegate?.manualPresenceChanged(authenticatedUser, presence: event.presence)
}
}
@@ -1,5 +1,5 @@
//
// ClientExtensions.swift
// Client+Utilities.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -23,26 +23,17 @@
import Foundation
extension Client {
public extension Client {
//MARK: - User & Channel
public func getChannelOrUserIdByName(name: String) -> String? {
if (name[name.startIndex] == "@") {
return getUserIdByName(name)
} else if (name[name.startIndex] == "C") {
return getChannelIDByName(name)
}
return nil
}
public func getChannelIDByName(name: String) -> String? {
return channels.filter{$0.1.name == stripString(name)}.first?.0
}
public func getUserIdByName(name: String) -> String? {
public func getUserIDByName(name: String) -> String? {
return users.filter{$0.1.name == stripString(name)}.first?.0
}
public func getImIDForUserWithID(id: String, success: (imID: String?)->Void, failure: (error: SlackError)->Void) {
let ims = channels.filter{$0.1.isIM == true}
let channel = ims.filter{$0.1.user == id}.first
@@ -52,34 +43,13 @@ extension Client {
webAPI.openIM(id, success: success, failure: failure)
}
}
//MARK: - Utilities
internal func stripString(var string: String) -> String {
if string[string.startIndex] == "@" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
} else if string[string.startIndex] == "#" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
internal func stripString(string: String) -> String? {
var strippedString = string
if string[string.startIndex] == "@" || string[string.startIndex] == "#" {
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
}
return string
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() -> String {
return NSNumber(double: timeIntervalSince1970).stringValue
}
}
+125 -80
View File
@@ -39,20 +39,21 @@ 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 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?
public var token = "SLACK_AUTH_TOKEN"
internal var token = "SLACK_AUTH_TOKEN"
public func setAuthToken(token: String) {
self.token = token
}
@@ -62,17 +63,25 @@ public class Client: WebSocketDelegate {
}
internal var webSocket: WebSocket?
private var dispatcher: EventDispatcher?
internal let api = NetworkInterface()
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 reconnect: Bool?
required public init(apiToken: String) {
self.token = apiToken
}
public func connect() {
dispatcher = EventDispatcher(client: self)
webAPI.rtmStart(success: {
public func connect(simpleLatest simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
webAPI.rtmStart(simpleLatest, noUnreads: noUnreads, mpimAware: mpimAware, success: {
(response) -> Void in
self.initialSetup(response)
if let socketURL = response["url"] as? String {
@@ -81,22 +90,29 @@ public class Client: WebSocketDelegate {
self.webSocket?.delegate = self
self.webSocket?.connect()
}
}, failure:nil)
}, failure: {(error) -> Void in
self.slackEventsDelegate?.clientConnectionFailed(error)
})
}
//MARK: - Message send
public func disconnect() {
webSocket?.disconnect()
}
//MARK: - RTM Message send
public func sendMessage(message: String, channelID: String) {
if (connected) {
if let data = formatMessageToSlackJsonString(msg: message, channel: channelID) {
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
webSocket?.writeString(string as! String)
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
}
}
}
}
private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
let json: [String: AnyObject] = [
"id": NSDate().timeIntervalSince1970,
"id": NSDate().slackTimestamp(),
"type": "message",
"channel": message.channel,
"text": message.msg.slackFormatEscaping()
@@ -120,75 +136,85 @@ public class Client: WebSocketDelegate {
sentMessages[ts!.stringValue] = 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 sendRTMPing() {
if connected {
let json: [String: AnyObject] = [
"id": NSDate().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)
}
}
catch _ {
}
}
}
private func timeoutCheck() -> Bool {
if let pong = pong, ping = ping, timeout = timeout {
if pong - ping < timeout {
return true
} else {
return false
}
// Ping-pong or timeout not configured
} else {
return true
}
}
//MARK: - Client setup
internal func initialSetup(json: [String: AnyObject]) {
private 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)
enumerateObjects(json["users"] as? Array) { (user) in self.addUser(user) }
enumerateObjects(json["channels"] as? Array) { (channel) in self.addChannel(channel) }
enumerateObjects(json["groups"] as? Array) { (group) in self.addChannel(group) }
enumerateObjects(json["mpims"] as? Array) { (mpim) in self.addChannel(mpim) }
enumerateObjects(json["ims"] as? Array) { (ims) in self.addChannel(ims) }
enumerateObjects(json["bots"] as? Array) { (bots) in self.addBot(bots) }
enumerateSubteams(json["subteams"] as? [String: AnyObject])
}
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: AnyObject]) {
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: AnyObject]) {
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: AnyObject]) {
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: AnyObject]?) {
if let subteams = subteams {
if let all = subteams["all"] as? [[String: AnyObject]] {
for item in all {
@@ -205,15 +231,32 @@ public class Client: WebSocketDelegate {
}
}
// MARK: - Utilities
private func enumerateObjects(array: [AnyObject]?, initalizer: ([String: AnyObject])-> Void) {
if let array = array {
for object in array {
if let dictionary = object as? [String: AnyObject] {
initalizer(dictionary)
}
}
}
}
// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {}
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = pingInterval {
pingRTMServerAtInterval(pingInterval)
}
}
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
connected = false
authenticated = false
webSocket = nil
if let delegate = slackEventsDelegate {
delegate.clientDisconnected()
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
}
}
@@ -222,7 +265,9 @@ public class Client: WebSocketDelegate {
return
}
do {
try dispatcher?.dispatch(NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject])
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
dispatch(json)
}
}
catch _ {
+10 -1
View File
@@ -1,5 +1,5 @@
//
// Message.swift
// Event.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -62,6 +62,7 @@ internal enum EventType: String {
case FileCommentDeleted = "file_comment_deleted"
case PinAdded = "pin_added"
case PinRemoved = "pin_removed"
case Pong = "pong"
case PresenceChange = "presence_change"
case ManualPresenceChange = "manual_presence_change"
case PrefChange = "pref_change"
@@ -78,10 +79,14 @@ internal enum EventType: String {
case TeamRename = "team_rename"
case TeamDomainChange = "team_domain_change"
case EmailDomainChange = "email_domain_change"
case TeamProfileChange = "team_profile_change"
case TeamProfileDelete = "team_profile_delete"
case TeamProfileReorder = "team_profile_reorder"
case BotAdded = "bot_added"
case BotChanged = "bot_changed"
case AccountsChanged = "accounts_changed"
case TeamMigrationStarted = "team_migration_started"
case ReconnectURL = "reconnect_url"
case SubteamCreated = "subteam_created"
case SubteamUpdated = "subteam_updated"
case SubteamSelfAdded = "subteam_self_added"
@@ -148,10 +153,12 @@ internal struct Event {
let file: File?
let message: Message?
let nestedMessage: Message?
let itemUser: String?
let item: Item?
let dndStatus: DoNotDisturbStatus?
let subteam: UserGroup?
let subteamID: String?
var profile: CustomProfile?
init(event:[String: AnyObject]) {
if let eventType = event["type"] as? String {
@@ -183,11 +190,13 @@ internal struct Event {
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])
itemUser = event["item_user"] as? String
item = Item(item: event["item"] as? [String: AnyObject])
subteam = UserGroup(userGroup: event["subteam"] as? [String: AnyObject])
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])
// Comment, Channel, User, and File can come across as Strings or Dictionaries
if (Comment(comment: event["comment"] as? [String: AnyObject])?.id == nil) {
+20 -13
View File
@@ -23,7 +23,8 @@
import Foundation
public protocol SlackEventsDelegate {
public protocol SlackEventsDelegate: class {
func clientConnectionFailed(error: SlackError)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
@@ -33,14 +34,14 @@ public protocol SlackEventsDelegate {
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 +53,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,21 +71,21 @@ 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 {
func reactionAdded(reaction: String?, item: Item?)
func reactionRemoved(reaction: String?, item: Item?)
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)
@@ -94,8 +95,14 @@ public protocol TeamEventsDelegate {
func teamEmojiChanged()
}
public protocol SubteamEventsDelegate {
public protocol SubteamEventsDelegate: class {
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
}
public protocol TeamProfileEventsDelegate: class {
func teamProfileChanged(profile: CustomProfile?)
func teamProfileDeleted(profile: CustomProfile?)
func teamProfileReordered(profile: CustomProfile?)
}
-604
View File
@@ -1,604 +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: - 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)
}
}
}
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)
}
}
}
//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: - Authenticated User
func manualPresenceChange(event: Event) {
client.authenticatedUser?.presence = event.presence
if let delegate = client.slackEventsDelegate {
delegate.manualPresenceChanged(client.authenticatedUser, presence: event.presence)
}
}
}
+59
View File
@@ -0,0 +1,59 @@
//
// 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 Foundation
public extension NSDate {
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}
}
internal extension String {
func slackFormatEscaping() -> String {
var escapedString = stringByReplacingOccurrencesOfString("&", withString: "&amp;")
escapedString = stringByReplacingOccurrencesOfString("<", withString: "&lt;")
escapedString = stringByReplacingOccurrencesOfString(">", withString: "&gt;")
return escapedString
}
}
internal extension Array {
func objectArrayFromDictionaryArray<T>(intializer:([String: AnyObject])->T?) -> [T] {
var returnValue = [T]()
for object in self {
if let dictionary = object as? [String: AnyObject] {
if let value = intializer(dictionary) {
returnValue.append(value)
}
}
}
return returnValue
}
}
+50 -2
View File
@@ -44,6 +44,22 @@ public struct File {
public let thumb360gif: String?
public let thumb360w: String?
public let thumb360h: String?
public let thumb480: String?
public let thumb480gif: String?
public let thumb480w: String?
public let thumb480h: String?
public let thumb720: String?
public let thumb720gif: String?
public let thumb720w: String?
public let thumb720h: String?
public let thumb960: String?
public let thumb960gif: String?
public let thumb960w: String?
public let thumb960h: String?
public let thumb1024: String?
public let thumb1024gif: String?
public let thumb1024w: String?
public let thumb1024h: String?
public let permalink: String?
public let editLink: String?
public let preview: String?
@@ -62,7 +78,7 @@ public struct File {
internal(set) public var comments = [String: Comment]()
internal(set) public var reactions = [String: Reaction]()
init?(file:[String: AnyObject]?) {
public init?(file:[String: AnyObject]?) {
id = file?["id"] as? String
created = file?["created"] as? Int
name = file?["name"] as? String
@@ -84,6 +100,22 @@ public struct File {
thumb360gif = file?["thumb_360_gif"] as? String
thumb360w = file?["thumb_360_w"] as? String
thumb360h = file?["thumb_360_h"] as? String
thumb480 = file?["thumb_480"] as? String
thumb480gif = file?["thumb_480_gif"] as? String
thumb480w = file?["thumb_480_w"] as? String
thumb480h = file?["thumb_480_h"] as? String
thumb720 = file?["thumb_720"] as? String
thumb720gif = file?["thumb_720_gif"] as? String
thumb720w = file?["thumb_720_w"] as? String
thumb720h = file?["thumb_720_h"] as? String
thumb960 = file?["thumb_960"] as? String
thumb960gif = file?["thumb_960_gif"] as? String
thumb960w = file?["thumb_960_w"] as? String
thumb960h = file?["thumb_960_h"] as? String
thumb1024 = file?["thumb_1024"] as? String
thumb1024gif = file?["thumb_1024_gif"] as? String
thumb1024w = file?["thumb_1024_w"] as? String
thumb1024h = file?["thumb_1024_h"] as? String
permalink = file?["permalink"] as? String
editLink = file?["edit_link"] as? String
preview = file?["preview"] as? String
@@ -105,7 +137,7 @@ public struct File {
}
init?(id:String?) {
internal init?(id:String?) {
self.id = id
created = nil
name = nil
@@ -126,6 +158,22 @@ public struct File {
thumb360gif = nil
thumb360w = nil
thumb360h = nil
thumb480 = nil
thumb480gif = nil
thumb480w = nil
thumb480h = nil
thumb720 = nil
thumb720gif = nil
thumb720w = nil
thumb720h = nil
thumb960 = nil
thumb960gif = nil
thumb960w = nil
thumb960h = nil
thumb1024 = nil
thumb1024gif = nil
thumb1024w = nil
thumb1024h = nil
permalink = nil
editLink = nil
preview = nil
+31 -12
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"
@@ -53,8 +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]?
init?(message: [String: AnyObject]?) {
public init?(message: [String: AnyObject]?) {
subtype = message?["subtype"] as? String
ts = message?["ts"] as? String
user = message?["user"] as? String
@@ -76,12 +70,37 @@ public class Message {
pinnedTo = message?["pinned_to"] as? [String]
comment = Comment(comment: message?["comment"] as? [String: AnyObject])
file = File(file: message?["file"] as? [String: AnyObject])
if let messageReactions = message?["reactions"] as? [[String: AnyObject]] {
for react in messageReactions {
let reaction = Reaction(reaction: react)
self.reactions[reaction!.name!] = reaction
reactions = messageReactions(message?["reactions"] as? [[String: AnyObject]])
attachments = (message?["attachments"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(attachment) -> Attachment? in
return Attachment(attachment: attachment)
})
}
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: [[String: AnyObject]]?) -> [String: Reaction] {
var returnValue = [String: Reaction]()
if let r = reactions {
for react in r {
if let reaction = Reaction(reaction: react), reactionName = reaction.name {
returnValue[reactionName] = reaction
}
}
}
return returnValue
}
}
+5 -2
View File
@@ -30,12 +30,13 @@ internal struct NetworkInterface {
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 = requestString + requestStringFromParameters(params)
requestString += requestStringFromParameters(params)
}
let request = NSURLRequest(URL: NSURL(string: requestString)!)
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
guard let data = data else {
errorClosure(SlackError.ClientNetworkError)
return
}
do {
@@ -119,7 +120,9 @@ internal struct NetworkInterface {
var requestString = ""
for key in parameters.keys {
if let value = parameters[key] as? String, encodedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet()) {
requestString = requestString + "&"+key+"="+encodedValue
requestString += "&\(key)=\(encodedValue)"
} else if let value = parameters[key] as? Int {
requestString += "&\(key)=\(value)"
}
}
+121 -13
View File
@@ -35,8 +35,14 @@ internal enum SlackAPIEndpoint: String {
case ChatDelete = "chat.delete"
case ChatPostMessage = "chat.postMessage"
case ChatUpdate = "chat.update"
case DNDInfo = "dnd.info"
case DNDTeamInfo = "dnd.teamInfo"
case EmojiList = "emoji.list"
case FilesCommentsAdd = "files.comments.add"
case FilesCommentsEdit = "files.comments.edit"
case FilesCommentsDelete = "files.comments.delete"
case FilesDelete = "files.delete"
case FilesInfo = "files.info"
case FilesUpload = "files.upload"
case GroupsClose = "groups.close"
case GroupsHistory = "groups.history"
@@ -105,8 +111,9 @@ public class SlackWebAPI {
}
//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: AnyObject])->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["simple_latest": simpleLatest, "no_unreads": noUnreads, "mpim_aware": mpimAware]
client.api.request(.RTMStart, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response: response)
}) {(error) -> Void in
@@ -125,10 +132,10 @@ 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: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
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: {
(history) -> Void in
success?(history:history)
success?(history: history)
}) {(error) -> Void in
failure?(error: error)
}
@@ -189,8 +196,8 @@ public class SlackWebAPI {
}
}
public func sendMessage(channel: String, text: String, username: String? = nil, asUser: Bool = false, parse: ParseMode = .Full, linkNames: Bool = false, attachments: [[String: AnyObject]]? = 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":attachments, "icon_url":iconURL, "icon_emoji":iconEmoji]
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: 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: {
(response) -> Void in
success?((ts: response["ts"] as? String, response["channel"] as? String))
@@ -199,8 +206,8 @@ public class SlackWebAPI {
}
}
public func updateMessage(channel: String, ts: String, message: String, attachments: [[String: AnyObject]]? = 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":attachments]
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: {
(response) -> Void in
success?(updated: true)
@@ -209,6 +216,27 @@ 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: {
(response) -> Void in
success?(status: DoNotDisturbStatus(status: response))
}) {(error) -> Void in
failure?(error: error)
}
}
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: {
(response) -> Void in
success?(statuses: self.enumerateDNDStauses(response["users"] as? [String: AnyObject]))
}) {(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: {
@@ -230,6 +258,23 @@ public class SlackWebAPI {
}
}
public func fileInfo(fileID: String, commentCount: Int = 100, totalPages: Int = 1, success: ((file: File?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "count": commentCount, "totalPages":totalPages]
client.api.request(.FilesInfo, token: client.token, parameters: parameters, successClosure: {
(response) in
var file = File(file: response["file"] as? [String: AnyObject])
(response["comments"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(comment) -> Comment? in
if let comment = Comment(comment: comment), id = comment.id {
file?.comments[id] = comment
}
return nil
})
success?(file: file)
}) {(error) in
failure?(error: error)
}
}
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: {
@@ -240,6 +285,37 @@ 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: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(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: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(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: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
failure?(error: error)
}
}
//MARK: - Groups
public func closeGroup(groupID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
close(.GroupsClose, channelID: groupID, success: {
@@ -250,7 +326,7 @@ 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: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
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: {
(history) -> Void in
success?(history: history)
@@ -324,7 +400,7 @@ 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: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
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: {
(history) -> Void in
success?(history: history)
@@ -372,7 +448,7 @@ 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: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
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: {
(history) -> Void in
success?(history: history)
@@ -573,11 +649,11 @@ 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: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
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: {
(response) -> Void in
success?(history: response)
success?(history: History(history: response))
}) {(error) -> Void in
failure?(error: error)
}
@@ -633,4 +709,36 @@ public class SlackWebAPI {
}
return finalParameters
}
//MARK: - Encode Attachments
private func encodeAttachments(attachments: [Attachment?]?) -> NSString? {
if let attachments = attachments {
var attachmentArray: [[String: AnyObject]] = []
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)
return string
} catch _ {
}
}
return nil
}
//MARK: - Enumerate Do Not Distrub Status
private func enumerateDNDStauses(statuses: [String: AnyObject]?) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
if let keys = statuses?.keys {
for key in keys {
retVal[key] = DoNotDisturbStatus(status: statuses?[key] as? [String: AnyObject])
}
}
return retVal
}
}
@@ -1,5 +1,5 @@
//
// SlackWebAPIErrorHandling.swift
// SlackWebAPIErrorDispatcher.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -34,6 +34,7 @@ public enum SlackError: ErrorType {
case BadRedirectURI
case BadTimeStamp
case CantArchiveGeneral
case CantDelete
case CantDeleteFile
case CantDeleteMessage
case CantInvite
@@ -75,6 +76,7 @@ public enum SlackError: ErrorType {
case MissingPostType
case NameTaken
case NoChannel
case NoComment
case NoItemSpecified
case NoReaction
case NoText
@@ -109,6 +111,8 @@ public enum SlackError: ErrorType {
case UserListNotSupplied
case UserNotFound
case UserNotVisible
// Client
case ClientNetworkError
}
internal struct ErrorDispatcher {
@@ -131,6 +135,8 @@ internal struct ErrorDispatcher {
return .BadRedirectURI
case "bad_timestamp":
return .BadTimeStamp
case "cant_delete":
return .CantDelete
case "cant_delete_file":
return .CantDeleteFile
case "cant_delete_message":
@@ -213,6 +219,8 @@ internal struct ErrorDispatcher {
return .NameTaken
case "no_channel":
return .NoChannel
case "no_comment":
return .NoComment
case "no_reaction":
return .NoReaction
case "no_item_specified":
+29 -4
View File
@@ -25,21 +25,46 @@ public struct Team {
public let id: String
internal(set) public var name: String?
internal(set) public var emailDomain: String?
internal(set) public var domain: String?
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 plan: String?
internal(set) public var icon: TeamIcon?
internal init?(team: [String: AnyObject]?) {
id = team?["id"] as! String
name = team?["id"] as? String
emailDomain = team?["email_domain"] as? String
name = team?["name"] as? String
domain = team?["domain"] as? String
messageEditWindowMinutes = team?["mesg_edit_window_mins"] as? Int
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]
plan = team?["plan"] as? String
icon = TeamIcon(icon: team?["icon"] as? [String: AnyObject])
}
}
public struct TeamIcon {
internal(set) public var image34: String?
internal(set) public var image44: String?
internal(set) public var image68: String?
internal(set) public var image88: String?
internal(set) public var image102: String?
internal(set) public var image132: String?
internal(set) public var imageOriginal: String?
internal(set) public var imageDefault: Bool?
internal init?(icon: [String: AnyObject]?) {
image34 = icon?["image_34"] as? String
image44 = icon?["image_44"] as? String
image68 = icon?["image_68"] as? String
image88 = icon?["image_88"] as? String
image102 = icon?["image_102"] as? String
image132 = icon?["image_132"] as? String
imageOriginal = icon?["image_original"] as? String
imageDefault = icon?["image_default"] as? Bool
}
}
+96
View File
@@ -21,6 +21,8 @@
// 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?
@@ -32,6 +34,27 @@ public struct Edited {
}
}
// MARK: - History
public struct History {
internal(set) public var latest: NSDate?
internal(set) public var messages = [Message]()
public let hasMore: Bool?
internal init?(history: [String: AnyObject]?) {
if let latestStr = history?["latest"] as? String, latestDouble = Double(latestStr) {
latest = NSDate(timeIntervalSince1970: NSTimeInterval(latestDouble))
}
if let msgs = history?["messages"] as? [[String: AnyObject]] {
for message in msgs {
if let message = Message(message: message) {
messages.append(message)
}
}
}
hasMore = history?["has_more"] as? Bool
}
}
// MARK: - Reaction
public struct Reaction {
public let name: String?
@@ -175,3 +198,76 @@ public struct DoNotDisturbStatus {
}
}
// MARK - Custom Team Profile
public struct CustomProfile {
internal(set) public var fields = [String: CustomProfileField]()
internal init?(profile: [String: AnyObject]?) {
if let eventFields = profile?["fields"] as? [AnyObject] {
for field in eventFields {
if let cpf = CustomProfileField(field: field as? [String: AnyObject]), id = cpf.id {
fields[id] = cpf
} else {
if let cpf = CustomProfileField(id: field as? String), id = cpf.id {
fields[id] = cpf
}
}
}
}
}
internal init?(customFields: [String: AnyObject]?) {
if let customFields = customFields {
for key in customFields.keys {
if let cpf = CustomProfileField(field: customFields[key] as? [String: AnyObject]) {
self.fields[key] = cpf
}
}
}
}
}
public struct CustomProfileField {
internal(set) public var id: String?
internal(set) public var alt: String?
internal(set) public var value: String?
internal(set) public var hidden: Bool?
internal(set) public var hint: String?
internal(set) public var label: String?
internal(set) public var options: String?
internal(set) public var ordering: Int?
internal(set) public var possibleValues: [String]?
internal(set) public var type: String?
internal init?(field: [String: AnyObject]?) {
id = field?["id"] as? String
alt = field?["alt"] as? String
value = field?["value"] as? String
hidden = field?["is_hidden"] as? Bool
hint = field?["hint"] as? String
label = field?["label"] as? String
options = field?["options"] as? String
ordering = field?["ordering"] as? Int
possibleValues = field?["possible_values"] as? [String]
type = field?["type"] as? String
}
internal init?(id: String?) {
self.id = id
}
internal mutating func updateProfileField(profile: CustomProfileField?) {
id = profile?.id != nil ? profile?.id : id
alt = profile?.alt != nil ? profile?.alt : alt
value = profile?.value != nil ? profile?.value : value
hidden = profile?.hidden != nil ? profile?.hidden : hidden
hint = profile?.hint != nil ? profile?.hint : hint
label = profile?.label != nil ? profile?.label : label
options = profile?.options != nil ? profile?.options : options
ordering = profile?.ordering != nil ? profile?.ordering : ordering
possibleValues = profile?.possibleValues != nil ? profile?.possibleValues : possibleValues
type = profile?.type != nil ? profile?.type : type
}
}
+3
View File
@@ -35,6 +35,7 @@ public struct User {
internal(set) public var image48: String?
internal(set) public var image72: String?
internal(set) public var image192: String?
internal(set) public var customProfile: CustomProfile?
internal init?(profile: [String: AnyObject]?) {
firstName = profile?["first_name"] as? String
@@ -48,9 +49,11 @@ 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])
}
}
public let id: String?
internal(set) public var name: String?
internal(set) public var deleted: Bool?
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.8</string>
<string>1.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>