Compare commits

...

28 Commits

Author SHA1 Message Date
Peter Zignego 7ab973a03b Include style in Action JSON dictionary 2016-08-02 23:21:16 -04:00
Peter Zignego 1889ffeeee Fix callback bug when authing via a provided token 2016-07-26 19:11:26 -04:00
Peter Zignego df661762a9 Bug fix 2016-07-23 11:09:54 -04:00
Peter Zignego e5a4a67bdb Fix imports for SPM 2016-07-18 00:07:03 -04:00
Peter Zignego 1e304f1f6d Update podfile.lock 2016-07-17 23:29:06 -04:00
Peter Zignego 81ebf86abd Update podfile and podspec 2016-07-17 23:04:03 -04:00
Peter Zignego afa16e98b3 Update podspec 2016-07-17 22:50:13 -04:00
Peter Zignego 6596bb2861 SlackKit 2.0.0 2016-07-17 22:20:15 -04:00
Peter Zignego fd12ab0600 Readme updates 2016-07-17 21:49:27 -04:00
Peter Zignego 31508e44dc Merge branch 'feature/message-buttons'
# Conflicts:
#	README.md
#	SlackKit.xcodeproj/project.pbxproj
#	SlackKit/Sources/MessageActionResponder.swift
#	SlackKit/Supporting Files/Info-iOS.plist
#	SlackKit/Supporting Files/Info-tvOS.plist
#	SlackKit/Supporting Files/Info.plist
2016-07-17 21:39:14 -04:00
Peter Zignego b980f371f6 SlackKit 2.0.0 2016-07-17 21:30:56 -04:00
Peter Zignego 9526d739e1 Oauth 2016-07-07 20:22:44 -04:00
Peter Zignego c3af5c33be Increment version 2016-07-04 14:35:51 -04:00
Peter Zignego c6258c57a0 Merge pull request #43 from pvzig/spm-fix
SPM fix
2016-07-04 14:32:31 -04:00
Peter Zignego 45e075aca2 Update readme 2016-07-04 14:30:52 -04:00
Peter Zignego 5ebe40a389 Remove example 2016-07-04 13:15:32 -04:00
Peter Zignego 0a5a8f83f7 Remove example target 2016-07-04 13:14:03 -04:00
Peter Zignego 8f4a287ad0 Fix Package.swift 2016-07-04 13:13:28 -04:00
Peter Zignego deadc8855a Webhook and server 2016-07-04 12:38:06 -04:00
Peter Zignego 98155f00b7 Remove bundled sample 2016-07-02 16:42:36 -04:00
Peter Zignego a55d3be65b Message buttons 2016-06-26 14:39:12 -04:00
Peter Zignego 3b8f1834f0 Code improvements 2016-06-25 18:52:53 -04:00
Peter Zignego 34972eb8af Types get their own .swift files 2016-06-25 18:51:48 -04:00
Peter Zignego f21179f567 Model type organization 2016-06-23 00:13:52 -04:00
Peter Zignego e1bf5a160f Merge pull request #41 from hamin/make-some-channel-setters-public
Make the following Chanel setters public 'unreadCountDisplay', 'unrea…
2016-06-19 22:00:07 -04:00
Haris Amin c66a5fe0db Make the following Chanel setters public 'unreadCountDisplay', 'unread', and 'lastRead' 2016-06-19 17:48:31 -04:00
Peter Zignego f03908bcc7 Merge pull request #38 from natestedman/master
Remove curly quotes from Carthage instructions
2016-06-04 11:13:55 -04:00
Nate Stedman 591b0d9d55 Remove curly quotes from Carthage instructions. 2016-06-04 08:55:23 -04:00
61 changed files with 2339 additions and 2075 deletions
+1
View File
@@ -0,0 +1 @@
2.2
+1
View File
@@ -1 +1,2 @@
github "daltoniam/Starscream" ~> 1.0
github "glock45/swifter" "d7c820bfc9260e469094b5f8d3d101b30c4a8fac"
+1
View File
@@ -1 +1,2 @@
github "daltoniam/Starscream" "1.1.3"
github "glock45/swifter" "d7c820bfc9260e469094b5f8d3d101b30c4a8fac"
@@ -1,58 +0,0 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
-680
View File
@@ -1,680 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6233" systemVersion="14A329f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6233"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OSX-Sample" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OSX-Sample" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About OSX-Sample" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide OSX-Sample" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit OSX-Sample" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="OSX-Sample Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="OSX-Sample" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>
-34
View File
@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Launch Software LLC. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
-160
View File
@@ -1,160 +0,0 @@
//
// 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)
}
}
+4 -2
View File
@@ -27,7 +27,9 @@ let package = Package(
name: "SlackKit",
targets: [],
dependencies: [
.Package(url: "https://github.com/pvzig/Starscream.git",
.Package(url: "https://github.com/daltoniam/Starscream",
majorVersion: 1),
]
.Package(url: "https://github.com/httpswift/swifter", majorVersion: 1)
],
exclude: ["Examples", "Carthage", "Pods"]
)
+6 -3
View File
@@ -2,14 +2,17 @@ source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'SlackKit' do
target 'SlackKit OS X' do
pod 'Starscream'
pod 'Swifter'
end
target 'SlackKit_iOS' do
target 'SlackKit iOS' do
pod 'Starscream'
pod 'Swifter'
end
target 'SlackKit_tvOS' do
target 'SlackKit tvOS' do
pod 'Starscream'
pod 'Swifter'
end
+5 -2
View File
@@ -1,12 +1,15 @@
PODS:
- Starscream (1.1.3)
- Swifter (1.2.6)
DEPENDENCIES:
- Starscream
- Swifter
SPEC CHECKSUMS:
Starscream: d662732354b40dd19ed1ece3e3c44c80b536b83c
Swifter: c6f13d053398f1e2f209c7a394dfd5e863ae0157
PODFILE CHECKSUM: d22778f772dbbded8b17fbebf5fa1c879d785aee
PODFILE CHECKSUM: 372d57254c34897042ce228cf421d7c9cc5caf9f
COCOAPODS: 1.0.0
COCOAPODS: 1.0.1
+181 -127
View File
@@ -1,13 +1,15 @@
![SlackKit](https://cloud.githubusercontent.com/assets/8311605/10260893/5ec60f96-694e-11e5-91fd-da6845942201.png)
##iOS, OS X, and tvOS Slack Client Library
###Description
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).
## SlackKit: A Swift Slack Client Library
### Description
This is a Slack client library for OS X, iOS, and tvOS written in Swift. It's intended to expose all of the functionality of Slack's [Real Time Messaging API](https://api.slack.com/rtm) as well as the [web APIs](https://api.slack.com/web) that are accessible to [bot users](https://api.slack.com/bot-users). SlackKit also supports Slacks [OAuth 2.0](https://api.slack.com/docs/oauth) flow including the [Add to Slack](https://api.slack.com/docs/slack-button) and [Sign in with Slack](https://api.slack.com/docs/sign-in-with-slack) buttons, [incoming webhooks](https://api.slack.com/incoming-webhooks), [slash commands](https://api.slack.com/slash-commands), and [message buttons](https://api.slack.com/docs/message-buttons).
####Building the SlackKit Framework
SlackKit also has alpha support for: [Swift 3](https://github.com/pvzig/SlackKit/tree/swift3), [Linux](https://github.com/pvzig/SlackKit/tree/linux)
#### 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
####CocoaPods
### Installation
#### CocoaPods
Add the pod to your podfile:
```
pod 'SlackKit'
@@ -17,11 +19,11 @@ and run
pod install
```
####Carthage
#### Carthage
Add SlackKit to your Cartfile:
```
github pvzig/SlackKit ~> 1.0
github "pvzig/SlackKit" ~> 2.0
```
and run
```
@@ -31,22 +33,19 @@ carthage bootstrap
```
carthage bootstrap --configuration "Debug"
```
Drag the built `SlackKit.framework` into your Xcode project.
####Swift Package Manager
#### 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)
.Package(url: "https://github.com/pvzig/SlackKit.git", majorVersion: 2)
]
)
```
Run `swift build` on your applications main directory.
To use the library in your project import it:
@@ -54,30 +53,87 @@ To use the library in your project import it:
import SlackKit
```
###Usage
To use SlackKit you'll need a bearer token which identifies a single user. You can generate a [full access token or create one using OAuth 2](https://api.slack.com/web).
### Usage
Once you have a token, initialize a client instance using it:
#### OAuth
Slack has [many different oauth scopes](https://api.slack.com/docs/oauth-scopes) that can be combined in different ways. If your application does not request the proper OAuth scopes, your API calls will fail.
If you authenticate using OAuth and the Add to Slack or Sign in with Slack buttons this is handled for you.
If you wish to make OAuth requests yourself, you can generate them using the `authorizeRequest` function on `SlackKit`s `oauth` property:
```swift
let client = Client(apiToken: "YOUR_SLACK_API_TOKEN")
func authorizeRequest(scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil)
```
If you want to receive messages from the Slack RTM API, connect to it.
For local development of things like OAuth, slash commands, and message buttons that require connecting over `https`, you may want to use a tool like [ngrok](https://ngrok.com) or [localtunnel](http://localtunnel.me).
#### Incoming Webhooks
After [configuring your incoming webhook in Slack](https://my.slack.com/services/new/incoming-webhook/), initialize IncomingWebhook with the provided URL and use `postMessage` to send messages.
```swift
client.connect()
let incoming = IncomingWebhook(url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX")
let message = Response(text: "Hello, World!")
incoming.postMessage(message)
```
#### Slash Commands
After [configuring your slash command in Slack](https://my.slack.com/services/new/slash-commands) (you can also provide slash commands as part of a [Slack App](https://api.slack.com/slack-apps)), initialize a webhook server with the token for the slash command, a configured route, and a response.
```swift
let response = Response(text: "Hello, World!", responseType: .InChannel)
let webhook = WebhookServer(token: "SLASH-COMMAND-TOKEN", route: "hello_world", response: response)
webhook.start()
```
When a user enters that slash command, it will hit your configured route and return the response you specified.
To add additional routes and responses, you can use WebhookServers addRoute function:
```swift
func addRoute(route: String, response: Response)
```
#### Message Buttons
If you are developing a Slack App and are authorizing using OAuth, you can use [message buttons](https://api.slack.com/docs/message-buttons).
To send messages with actions, add them to an attachment:
```swift
let helloAction = Action(name: "hello_world", text: "Hello, World!")
let attachment = Attachment(fallback: "Hello World Attachment", title: "Attachment with an Action Button", callbackID: "helloworld", actions: [helloAction])
```
To act on message actions, initialize an instance of the `MessageActionServer` using your apps verification token, your specified interactive messages request URL route, and a `MessageActionResponder`:
```swift
let action = Action(name: "hello_world", text: "Hello, World!")
let response = Response(text: "Hello, 🌎!", responseType: .InChannel)
let responder = MessageActionResponder(responses: [(action, response)])
let server = MessageActionServer(token: "SLACK-APP-VERIFICATION-TOKEN", route: "actions", responder: responder)
server.start()
```
#### Bot Users
To deploy a bot user using SlackKit you'll need a bearer token which identifies a single user. You can generate a [full access token or create one using OAuth 2](https://api.slack.com/web).
Initialize a SlackKit instance using your [applications Client ID and Client Secret](https://api.slack.com/apps) to set up SlackKit for OAuth authorization:
```swift
let bot = SlackKit(clientID: "CLIENT_ID", clientSecret: "CLIENT_SECRET")
```
or use a manually acquired token:
```swift
let bot = SlackKit(withAPIToken: "xoxp-YOUR-SLACK-API-TOKEN")
```
#### Client Connection Options
You can also set options for a ping/pong interval, timeout interval, and automatic reconnection:
```swift
client.connect(pingInterval: 2, timeout: 10, reconnect: false)
let options = ClientOptions(pingInterval: 2, timeout: 10, reconnect: false)
let bot = SlackKit(clientID: "CLIENT_ID", clientSecret: "CLIENT_SECRET", clientOptions: options)
```
Once connected, the client will begin to consume any messages sent by the Slack RTM API.
####Web API Methods
#### Web API Methods
SlackKit currently supports the a subset of the Slack Web APIs that are available to bot users:
- api.test
- auth.revoke
- auth.test
- channels.history
- channels.info
@@ -113,6 +169,7 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- mpim.list
- mpim.mark
- mpim.open
- oauth.access
- pins.add
- pins.list
- pins.remove
@@ -132,125 +189,122 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
They can be accessed through a Client objects `webAPI` property:
```swift
client.webAPI.authenticationTest({
(authenticated) -> Void in
print(authenticated)
}){(error) -> Void in
print(error)
client.webAPI.authenticationTest({ (authenticated) -> Void in
print(authenticated)
}){(error) -> Void in
print(error)
}
```
####Delegate methods
#### Delegate methods
To receive delegate callbacks for certain events, register an object as the delegate for those events:
To receive delegate callbacks for events, register an object as the delegate for those events using the `onClientInitalization` block:
```swift
client.slackEventsDelegate = self
let bot = SlackKit(clientID: "CLIENT_ID", clientSecret: "CLIENT_SECRET")
bot.onClientInitalization = { (client: Client) in
dispatch_async(dispatch_get_main_queue(), {
client.connectionEventsDelegate = self
client.messageEventsDelegate = self
})
}
```
Delegate callbacks contain a reference to the Client where the event occurred.
There are a number of delegates that you can set to receive callbacks for certain events.
#####SlackEventsDelegate
##### ConnectionEventsDelegate
```swift
func clientConnectionFailed(error: SlackError)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
func userChanged(user: User)
func presenceChanged(user: User?, presence: String?)
func manualPresenceChanged(user: User?, presence: String?)
func botEvent(bot: Bot)
clientConnected(client: Client)
clientDisconnected(client: Client)
clientConnectionFailed(client: Client, error: SlackError)
```
##### MessageEventsDelegate
```swift
messageSent(client: Client, message: Message)
messageReceived(client: Client, message: Message)
messageChanged(client: Client, message: Message)
messageDeleted(client: Client, message: Message?)
```
##### ChannelEventsDelegate
```swift
userTyping(client: Client, channel: Channel, user: User)
channelMarked(client: Client, channel: Channel, timestamp: String)
channelCreated(client: Client, channel: Channel)
channelDeleted(client: Client, channel: Channel)
channelRenamed(client: Client, channel: Channel)
channelArchived(client: Client, channel: Channel)
channelHistoryChanged(client: Client, channel: Channel)
channelJoined(client: Client, channel: Channel)
channelLeft(client: Client, channel: Channel)
```
##### DoNotDisturbEventsDelegate
```swift
doNotDisturbUpdated(client: Client, dndStatus: DoNotDisturbStatus)
doNotDisturbUserUpdated(client: Client, dndStatus: DoNotDisturbStatus, user: User)
```
##### GroupEventsDelegate
```swift
groupOpened(client: Client, group: Channel)
```
##### FileEventsDelegate
```swift
fileProcessed(client: Client, file: File)
fileMadePrivate(client: Client, file: File)
fileDeleted(client: Client, file: File)
fileCommentAdded(client: Client, file: File, comment: Comment)
fileCommentEdited(client: Client, file: File, comment: Comment)
fileCommentDeleted(client: Client, file: File, comment: Comment)
```
##### PinEventsDelegate
```swift
itemPinned(client: Client, item: Item, channel: Channel?)
itemUnpinned(client: Client, item: Item, channel: Channel?)
```
##### StarEventsDelegate
```swift
itemStarred(client: Client, item: Item, star: Bool)
```
##### ReactionEventsDelegate
```swift
reactionAdded(client: Client, reaction: String, item: Item, itemUser: String)
reactionRemoved(client: Client, reaction: String, item: Item, itemUser: String)
```
##### SlackEventsDelegate
```swift
preferenceChanged(client: Client, preference: String, value: AnyObject?)
userChanged(client: Client, user: User)
presenceChanged(client: Client, user: User, presence: String)
manualPresenceChanged(client: Client, user: User, presence: String)
botEvent(client: Client, bot: Bot)
```
##### TeamEventsDelegate
```swift
teamJoined(client: Client, user: User)
teamPlanChanged(client: Client, plan: String)
teamPreferencesChanged(client: Client, preference: String, value: AnyObject?)
teamNameChanged(client: Client, name: String)
teamDomainChanged(client: Client, domain: String)
teamEmailDomainChanged(client: Client, domain: String)
teamEmojiChanged(client: Client)
```
##### SubteamEventsDelegate
```swift
subteamEvent(client: Client, userGroup: UserGroup)
subteamSelfAdded(client: Client, subteamID: String)
subteamSelfRemoved(client: Client, subteamID: String)
```
##### TeamProfileEventsDelegate
```swift
teamProfileChanged(client: Client, profile: CustomProfile)
teamProfileDeleted(client: Client, profile: CustomProfile)
teamProfileReordered(client: Client, profile: CustomProfile)
```
#####MessageEventsDelegate
```swift
func messageSent(message: Message)
func messageReceived(message: Message)
func messageChanged(message: Message)
func messageDeleted(message: Message?)
```
### Examples
[Check out example applications here.](https://github.com/pvzig/SlackKit-examples)
#####ChannelEventsDelegate
```swift
func userTyping(channel: Channel?, user: User?)
func channelMarked(channel: Channel, timestamp: String?)
func channelCreated(channel: Channel)
func channelDeleted(channel: Channel)
func channelRenamed(channel: Channel)
func channelArchived(channel: Channel)
func channelHistoryChanged(channel: Channel)
func channelJoined(channel: Channel)
func channelLeft(channel: Channel)
```
#####DoNotDisturbEventsDelegate
```swift
doNotDisturbUpdated(dndStatus: DoNotDisturbStatus)
doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User?)
```
#####GroupEventsDelegate
```swift
func groupOpened(group: Channel)
```
#####FileEventsDelegate
```swift
func fileProcessed(file: File)
func fileMadePrivate(file: File)
func fileDeleted(file: File)
func fileCommentAdded(file: File, comment: Comment)
func fileCommentEdited(file: File, comment: Comment)
func fileCommentDeleted(file: File, comment: Comment)
```
#####PinEventsDelegate
```swift
func itemPinned(item: Item?, channel: Channel?)
func itemUnpinned(item: Item?, channel: Channel?)
```
#####StarEventsDelegate
```swift
func itemStarred(item: Item, star: Bool)
```
#####ReactionEventsDelegate
```swift
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
```
#####TeamEventsDelegate
```swift
func teamJoined(user: User)
func teamPlanChanged(plan: String)
func teamPreferencesChanged(preference: String, value: AnyObject)
func teamNameChanged(name: String)
func teamDomainChanged(domain: String)
func teamEmailDomainChanged(domain: String)
func teamEmojiChanged()
```
#####SubteamEventsDelegate
```swift
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
```
###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
### Get In Touch
[@pvzig](https://twitter.com/pvzig)
<peter@launchsoft.co>
+2 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "1.1.1"
s.version = "2.0.0"
s.summary = "a Slack client library for OS X, iOS, and tvOS written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
@@ -14,5 +14,6 @@ Pod::Spec.new do |s|
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.1.3'
s.dependency 'Swifter'
end
+285 -151
View File
@@ -7,30 +7,26 @@
objects = {
/* Begin PBXBuildFile section */
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
2601D6271C7688610012BF22 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D6261C7688610012BF22 /* AppDelegate.swift */; };
2601D6291C7688610012BF22 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2601D6281C7688610012BF22 /* Assets.xcassets */; };
2601D62C1C7688610012BF22 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2601D62A1C7688610012BF22 /* MainMenu.xib */; };
2601D61B1C7646B80012BF22 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
260EC2351C4DC61D0093B253 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993901CE90C87004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993981CE90EE0004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993991CE90EE0004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
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 */; };
263993A01CE90EE0004A6E93 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993A21CE90EE0004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993A31CE90EE0004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993A61CE90EE0004A6E93 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
@@ -39,24 +35,69 @@
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 */; };
263993BF1CE90EED004A6E93 /* WebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* WebAPI.swift */; };
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993C11CE90EED004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993C21CE90EED004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993C51CE90EED004A6E93 /* SlackError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackError.swift */; };
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
2678B5941D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5951D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5961D3151B900CE521A /* AuthorizeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2678B5931D3151B900CE521A /* AuthorizeResponse.swift */; };
2678B5971D3151C600CE521A /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
2678B5981D3151C900CE521A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
2678B5991D3151CD00CE521A /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
269B475A1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475B1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475C1D3493DE0042D137 /* OAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47591D3493DE0042D137 /* OAuthResponse.swift */; };
269B475E1D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B475F1D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B47601D3538E90042D137 /* SlackKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B475D1D3538E90042D137 /* SlackKit.swift */; };
269B47621D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47631D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47641D3544240042D137 /* IncomingWebhook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47611D3544240042D137 /* IncomingWebhook.swift */; };
269B47661D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47671D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47681D39AAA80042D137 /* MessageActionResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47651D39AAA80042D137 /* MessageActionResponder.swift */; };
269B47C71D3AE25B0042D137 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
269B47C81D3AE25B0042D137 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
269B47C91D3AE2620042D137 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
269B47CA1D3AE2670042D137 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
269B47CC1D3AE5670042D137 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 269B47CB1D3AE5670042D137 /* Swifter.framework */; };
269B47CE1D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
269B47CF1D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
269B47D01D3C22FC0042D137 /* ClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269B47CD1D3C22FC0042D137 /* ClientOptions.swift */; };
26B30B6C1D289FA0004D4AB5 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B30B6B1D289FA0004D4AB5 /* Swifter.framework */; };
26B30B6F1D289FB2004D4AB5 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B30B6E1D289FB2004D4AB5 /* Swifter.framework */; };
26B30B881D297A98004D4AB5 /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
26B30B901D298E08004D4AB5 /* MessageActionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */; };
26B30B921D298E12004D4AB5 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
26B30B941D298E78004D4AB5 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
26B30B951D298E78004D4AB5 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B931D298E78004D4AB5 /* Response.swift */; };
26B30B961D298EE1004D4AB5 /* MessageActionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */; };
26B30B971D298EED004D4AB5 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B911D298E12004D4AB5 /* Server.swift */; };
26B30B991D298F25004D4AB5 /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
26B30B9A1D298F25004D4AB5 /* WebhookRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30B981D298F25004D4AB5 /* WebhookRequest.swift */; };
26B30BB61D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BB71D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BB81D2BC2E4004D4AB5 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BB51D2BC2E4004D4AB5 /* Scope.swift */; };
26B30BC31D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC41D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC51D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */; };
26B30BC71D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26B30BC81D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26B30BC91D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */; };
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
@@ -65,11 +106,47 @@
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
26BBA19E1C398E3C00BF7225 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.swift */; };
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
26F4BAC31C9DEBD1000910BA /* Leaderboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */; };
26EC14C11D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C21D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C31D1EF16500FD3A53 /* AttachmentField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */; };
26EC14C91D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CA1D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CB1D1EF17400FD3A53 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C41D1EF17400FD3A53 /* Comment.swift */; };
26EC14CC1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CD1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CE1D1EF17400FD3A53 /* CustomProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */; };
26EC14CF1D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D01D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D11D1EF17400FD3A53 /* CustomProfileField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */; };
26EC14D21D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D31D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D41D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */; };
26EC14D51D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14D61D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14D71D1EF17400FD3A53 /* Edited.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14C81D1EF17400FD3A53 /* Edited.swift */; };
26EC14DA1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DB1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DC1D1EF17E00FD3A53 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D81D1EF17E00FD3A53 /* History.swift */; };
26EC14DD1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14DE1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14DF1D1EF17E00FD3A53 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14D91D1EF17E00FD3A53 /* Item.swift */; };
26EC14E11D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E21D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E31D1EF18700FD3A53 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E01D1EF18700FD3A53 /* Reaction.swift */; };
26EC14E61D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E71D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E81D1EF18F00FD3A53 /* TeamIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */; };
26EC14E91D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14EA1D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14EB1D1EF18F00FD3A53 /* Topic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14E51D1EF18F00FD3A53 /* Topic.swift */; };
26EC14F91D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC14FA1D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC14FB1D1F355A00FD3A53 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC14F81D1F355A00FD3A53 /* Action.swift */; };
26EC15011D260B1000FD3A53 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
26EC15021D260B1000FD3A53 /* WebhookServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EC15001D260B1000FD3A53 /* WebhookServer.swift */; };
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 */; };
@@ -77,22 +154,34 @@
/* 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>"; };
2601D61A1C7646B80012BF22 /* SlackError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackError.swift; path = Sources/SlackError.swift; sourceTree = "<group>"; };
26072A341BB48B3A00CD650C /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
260EC2301C4DC61D0093B253 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = "<group>"; };
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkInterface.swift; path = Sources/NetworkInterface.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPI.swift; path = Sources/SlackWebAPI.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* WebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebAPI.swift; path = Sources/WebAPI.swift; sourceTree = "<group>"; };
263993B21CE90EE0004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
263993D11CE90EED004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2661A6A41BBF62FF0026F67B /* SlackKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SlackKit.h; sourceTree = "<group>"; };
2678B5931D3151B900CE521A /* AuthorizeResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthorizeResponse.swift; path = Sources/AuthorizeResponse.swift; sourceTree = "<group>"; };
268E46131CE8F79D009F19CC /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-iOS.plist"; path = "Supporting Files/Info-iOS.plist"; sourceTree = "<group>"; };
268E46141CE8F79D009F19CC /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-tvOS.plist"; path = "Supporting Files/Info-tvOS.plist"; sourceTree = "<group>"; };
268E46151CE8F79D009F19CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Supporting Files/Info.plist"; sourceTree = "<group>"; };
269B47591D3493DE0042D137 /* OAuthResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OAuthResponse.swift; path = Sources/OAuthResponse.swift; sourceTree = "<group>"; };
269B475D1D3538E90042D137 /* SlackKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackKit.swift; path = Sources/SlackKit.swift; sourceTree = "<group>"; };
269B47611D3544240042D137 /* IncomingWebhook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IncomingWebhook.swift; path = Sources/IncomingWebhook.swift; sourceTree = "<group>"; };
269B47651D39AAA80042D137 /* MessageActionResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionResponder.swift; path = Sources/MessageActionResponder.swift; sourceTree = "<group>"; };
269B47CB1D3AE5670042D137 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/tvOS/Swifter.framework; sourceTree = "<group>"; };
269B47CD1D3C22FC0042D137 /* ClientOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClientOptions.swift; path = Sources/ClientOptions.swift; sourceTree = "<group>"; };
26B30B6B1D289FA0004D4AB5 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/Mac/Swifter.framework; sourceTree = "<group>"; };
26B30B6E1D289FB2004D4AB5 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/iOS/Swifter.framework; sourceTree = "<group>"; };
26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionRequest.swift; path = Sources/MessageActionRequest.swift; sourceTree = "<group>"; };
26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageActionServer.swift; path = Sources/MessageActionServer.swift; sourceTree = "<group>"; };
26B30B911D298E12004D4AB5 /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Server.swift; path = Sources/Server.swift; sourceTree = "<group>"; };
26B30B931D298E78004D4AB5 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Sources/Response.swift; sourceTree = "<group>"; };
26B30B981D298F25004D4AB5 /* WebhookRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebhookRequest.swift; path = Sources/WebhookRequest.swift; sourceTree = "<group>"; };
26B30BB51D2BC2E4004D4AB5 /* Scope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Scope.swift; path = Sources/Scope.swift; sourceTree = "<group>"; };
26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OAuthServer.swift; path = Sources/OAuthServer.swift; sourceTree = "<group>"; };
26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthorizeRequest.swift; path = Sources/AuthorizeRequest.swift; sourceTree = "<group>"; };
26BBA1871C398E3C00BF7225 /* Bot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bot.swift; path = Sources/Bot.swift; sourceTree = "<group>"; };
26BBA1881C398E3C00BF7225 /* Channel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Channel.swift; path = Sources/Channel.swift; sourceTree = "<group>"; };
26BBA1891C398E3C00BF7225 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Client.swift; path = Sources/Client.swift; sourceTree = "<group>"; };
@@ -101,11 +190,22 @@
26BBA18E1C398E3C00BF7225 /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = File.swift; path = Sources/File.swift; sourceTree = "<group>"; };
26BBA18F1C398E3C00BF7225 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Message.swift; path = Sources/Message.swift; sourceTree = "<group>"; };
26BBA1901C398E3C00BF7225 /* Team.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Team.swift; path = Sources/Team.swift; sourceTree = "<group>"; };
26BBA1911C398E3C00BF7225 /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Types.swift; path = Sources/Types.swift; sourceTree = "<group>"; };
26BBA1921C398E3C00BF7225 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = Sources/User.swift; sourceTree = "<group>"; };
26BBA1931C398E3C00BF7225 /* UserGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserGroup.swift; path = Sources/UserGroup.swift; sourceTree = "<group>"; };
26DF40341C7A0FA300E19241 /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Attachment.swift; path = Sources/Attachment.swift; sourceTree = "<group>"; };
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Leaderboard.swift; sourceTree = "<group>"; };
26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentField.swift; path = Sources/AttachmentField.swift; sourceTree = "<group>"; };
26EC14C41D1EF17400FD3A53 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Comment.swift; path = Sources/Comment.swift; sourceTree = "<group>"; };
26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomProfile.swift; path = Sources/CustomProfile.swift; sourceTree = "<group>"; };
26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomProfileField.swift; path = Sources/CustomProfileField.swift; sourceTree = "<group>"; };
26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DoNotDisturbStatus.swift; path = Sources/DoNotDisturbStatus.swift; sourceTree = "<group>"; };
26EC14C81D1EF17400FD3A53 /* Edited.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Edited.swift; path = Sources/Edited.swift; sourceTree = "<group>"; };
26EC14D81D1EF17E00FD3A53 /* History.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = History.swift; path = Sources/History.swift; sourceTree = "<group>"; };
26EC14D91D1EF17E00FD3A53 /* Item.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Item.swift; path = Sources/Item.swift; sourceTree = "<group>"; };
26EC14E01D1EF18700FD3A53 /* Reaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Reaction.swift; path = Sources/Reaction.swift; sourceTree = "<group>"; };
26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TeamIcon.swift; path = Sources/TeamIcon.swift; sourceTree = "<group>"; };
26EC14E51D1EF18F00FD3A53 /* Topic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Topic.swift; path = Sources/Topic.swift; sourceTree = "<group>"; };
26EC14F81D1F355A00FD3A53 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Sources/Action.swift; sourceTree = "<group>"; };
26EC15001D260B1000FD3A53 /* WebhookServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebhookServer.swift; path = Sources/WebhookServer.swift; sourceTree = "<group>"; };
4307A07F1CC6D0910011D5DE /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/Mac/Starscream.framework; sourceTree = "<group>"; };
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+Utilities.swift"; path = "Sources/Client+Utilities.swift"; sourceTree = "<group>"; };
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventDispatching.swift"; path = "Sources/Client+EventDispatching.swift"; sourceTree = "<group>"; };
@@ -113,18 +213,12 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2601D6211C7688610012BF22 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A301BB48B3A00CD650C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */,
26B30B6C1D289FA0004D4AB5 /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -133,6 +227,7 @@
buildActionMask = 2147483647;
files = (
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */,
26B30B6F1D289FB2004D4AB5 /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -141,29 +236,17 @@
buildActionMask = 2147483647;
files = (
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */,
269B47CC1D3AE5670042D137 /* Swifter.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2601D6251C7688610012BF22 /* OSX-Sample */ = {
isa = PBXGroup;
children = (
2601D6261C7688610012BF22 /* AppDelegate.swift */,
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */,
2601D6281C7688610012BF22 /* Assets.xcassets */,
2601D62A1C7688610012BF22 /* MainMenu.xib */,
2601D62D1C7688610012BF22 /* Info.plist */,
);
path = "OSX-Sample";
sourceTree = "<group>";
};
26072A2A1BB48B3A00CD650C = {
isa = PBXGroup;
children = (
2661A6811BBF60E60026F67B /* SlackKit */,
2601D6251C7688610012BF22 /* OSX-Sample */,
26072A351BB48B3A00CD650C /* Products */,
CA70A3A1A9A1A259960DFBCF /* Frameworks */,
);
@@ -173,7 +256,6 @@
isa = PBXGroup;
children = (
26072A341BB48B3A00CD650C /* SlackKit.framework */,
2601D6241C7688610012BF22 /* OSX-Sample.app */,
263993B21CE90EE0004A6E93 /* SlackKit.framework */,
263993D11CE90EED004A6E93 /* SlackKit.framework */,
);
@@ -183,25 +265,22 @@
2661A6811BBF60E60026F67B /* SlackKit */ = {
isa = PBXGroup;
children = (
26DF40341C7A0FA300E19241 /* Attachment.swift */,
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
26BBA1891C398E3C00BF7225 /* Client.swift */,
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */,
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */,
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */,
260EC2301C4DC61D0093B253 /* Extensions.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
269B47611D3544240042D137 /* IncomingWebhook.swift */,
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */,
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */,
2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */,
26BBA1901C398E3C00BF7225 /* Team.swift */,
26BBA1911C398E3C00BF7225 /* Types.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
26B30B8F1D298E08004D4AB5 /* MessageActionServer.swift */,
269B47651D39AAA80042D137 /* MessageActionResponder.swift */,
26B30BC21D2DFF0D004D4AB5 /* OAuthServer.swift */,
26B30B911D298E12004D4AB5 /* Server.swift */,
269B475D1D3538E90042D137 /* SlackKit.swift */,
260EC2321C4DC61D0093B253 /* WebAPI.swift */,
26EC15001D260B1000FD3A53 /* WebhookServer.swift */,
26EC14B31D1B974500FD3A53 /* Models */,
268E46161CE8F7A2009F19CC /* Supporting Files */,
);
path = SlackKit;
@@ -218,9 +297,65 @@
name = "Supporting Files";
sourceTree = "<group>";
};
26B30B9B1D29AB1E004D4AB5 /* Server */ = {
isa = PBXGroup;
children = (
26B30B871D297A98004D4AB5 /* MessageActionRequest.swift */,
26B30B931D298E78004D4AB5 /* Response.swift */,
26B30B981D298F25004D4AB5 /* WebhookRequest.swift */,
);
name = Server;
sourceTree = "<group>";
};
26B30BB91D2BC2EA004D4AB5 /* Auth */ = {
isa = PBXGroup;
children = (
26B30BC61D2DFF7D004D4AB5 /* AuthorizeRequest.swift */,
2678B5931D3151B900CE521A /* AuthorizeResponse.swift */,
26B30BB51D2BC2E4004D4AB5 /* Scope.swift */,
269B47591D3493DE0042D137 /* OAuthResponse.swift */,
);
name = Auth;
sourceTree = "<group>";
};
26EC14B31D1B974500FD3A53 /* Models */ = {
isa = PBXGroup;
children = (
26EC14F81D1F355A00FD3A53 /* Action.swift */,
26DF40341C7A0FA300E19241 /* Attachment.swift */,
26EC14C01D1EF16500FD3A53 /* AttachmentField.swift */,
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
269B47CD1D3C22FC0042D137 /* ClientOptions.swift */,
26EC14C41D1EF17400FD3A53 /* Comment.swift */,
26EC14C51D1EF17400FD3A53 /* CustomProfile.swift */,
26EC14C61D1EF17400FD3A53 /* CustomProfileField.swift */,
26EC14C71D1EF17400FD3A53 /* DoNotDisturbStatus.swift */,
26EC14C81D1EF17400FD3A53 /* Edited.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26EC14D81D1EF17E00FD3A53 /* History.swift */,
26EC14D91D1EF17E00FD3A53 /* Item.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
26EC14E01D1EF18700FD3A53 /* Reaction.swift */,
2601D61A1C7646B80012BF22 /* SlackError.swift */,
26BBA1901C398E3C00BF7225 /* Team.swift */,
26EC14E41D1EF18F00FD3A53 /* TeamIcon.swift */,
26EC14E51D1EF18F00FD3A53 /* Topic.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
26B30BB91D2BC2EA004D4AB5 /* Auth */,
26B30B9B1D29AB1E004D4AB5 /* Server */,
);
name = Models;
sourceTree = "<group>";
};
CA70A3A1A9A1A259960DFBCF /* Frameworks */ = {
isa = PBXGroup;
children = (
269B47CB1D3AE5670042D137 /* Swifter.framework */,
26B30B6E1D289FB2004D4AB5 /* Swifter.framework */,
26B30B6B1D289FA0004D4AB5 /* Swifter.framework */,
4307A07F1CC6D0910011D5DE /* Starscream.framework */,
);
name = Frameworks;
@@ -256,23 +391,6 @@
/* 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 OS X */ = {
isa = PBXNativeTarget;
buildConfigurationList = 26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit OS X" */;
@@ -333,13 +451,10 @@
26072A2B1BB48B3A00CD650C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0700;
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = "Launch Software LLC";
TargetAttributes = {
2601D6231C7688610012BF22 = {
CreatedOnToolsVersion = 7.2.1;
};
26072A331BB48B3A00CD650C = {
CreatedOnToolsVersion = 7.0;
};
@@ -361,21 +476,11 @@
26072A331BB48B3A00CD650C /* SlackKit OS X */,
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;
@@ -400,36 +505,53 @@
/* End PBXResourcesBuildPhase 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 = (
2678B5941D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26B30B921D298E12004D4AB5 /* Server.swift in Sources */,
269B47CE1D3C22FC0042D137 /* ClientOptions.swift in Sources */,
269B47621D3544240042D137 /* IncomingWebhook.swift in Sources */,
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */,
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */,
26B30B901D298E08004D4AB5 /* MessageActionServer.swift in Sources */,
26B30B941D298E78004D4AB5 /* Response.swift in Sources */,
26EC14CF1D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
269B47661D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */,
26BBA19E1C398E3C00BF7225 /* Types.swift in Sources */,
26EC14DD1D1EF17E00FD3A53 /* Item.swift in Sources */,
26EC15011D260B1000FD3A53 /* WebhookServer.swift in Sources */,
26EC14CC1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */,
26B30B881D297A98004D4AB5 /* MessageActionRequest.swift in Sources */,
269B475E1D3538E90042D137 /* SlackKit.swift in Sources */,
26B30BC71D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */,
26EC14D21D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */,
26EC14E61D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */,
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */,
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */,
26EC14F91D1F355A00FD3A53 /* Action.swift in Sources */,
26B30BC31D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
26EC14D51D1EF17400FD3A53 /* Edited.swift in Sources */,
260EC2351C4DC61D0093B253 /* WebAPI.swift in Sources */,
26EC14E11D1EF18700FD3A53 /* Reaction.swift in Sources */,
26B30BB61D2BC2E4004D4AB5 /* Scope.swift in Sources */,
26EC14E91D1EF18F00FD3A53 /* Topic.swift in Sources */,
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */,
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */,
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */,
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */,
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackError.swift in Sources */,
26EC14DA1D1EF17E00FD3A53 /* History.swift in Sources */,
269B475A1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */,
26B30B991D298F25004D4AB5 /* WebhookRequest.swift in Sources */,
26EC14C91D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C11D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */,
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */,
);
@@ -439,23 +561,49 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26B30B961D298EE1004D4AB5 /* MessageActionRequest.swift in Sources */,
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */,
269B47CF1D3C22FC0042D137 /* ClientOptions.swift in Sources */,
269B47C71D3AE25B0042D137 /* MessageActionServer.swift in Sources */,
263993981CE90EE0004A6E93 /* Channel.swift in Sources */,
26EC14D01D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
263993991CE90EE0004A6E93 /* User.swift in Sources */,
2639939A1CE90EE0004A6E93 /* Types.swift in Sources */,
26EC14DE1D1EF17E00FD3A53 /* Item.swift in Sources */,
269B475B1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
26EC15021D260B1000FD3A53 /* WebhookServer.swift in Sources */,
26B30BC41D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
26EC14CD1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */,
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */,
26B30BC81D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
2678B5951D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26EC14D31D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */,
26EC14E71D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */,
26B30B971D298EED004D4AB5 /* Server.swift in Sources */,
2639939F1CE90EE0004A6E93 /* File.swift in Sources */,
263993A01CE90EE0004A6E93 /* SlackWebAPI.swift in Sources */,
26B30B951D298E78004D4AB5 /* Response.swift in Sources */,
26EC14FA1D1F355A00FD3A53 /* Action.swift in Sources */,
26EC14D61D1EF17400FD3A53 /* Edited.swift in Sources */,
263993A01CE90EE0004A6E93 /* WebAPI.swift in Sources */,
26B30B9A1D298F25004D4AB5 /* WebhookRequest.swift in Sources */,
26EC14E21D1EF18700FD3A53 /* Reaction.swift in Sources */,
26EC14EA1D1EF18F00FD3A53 /* Topic.swift in Sources */,
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */,
26B30BB71D2BC2E4004D4AB5 /* Scope.swift in Sources */,
263993A21CE90EE0004A6E93 /* Message.swift in Sources */,
263993A31CE90EE0004A6E93 /* Team.swift in Sources */,
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */,
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */,
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
263993A61CE90EE0004A6E93 /* SlackError.swift in Sources */,
269B475F1D3538E90042D137 /* SlackKit.swift in Sources */,
26EC14DB1D1EF17E00FD3A53 /* History.swift in Sources */,
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */,
269B47631D3544240042D137 /* IncomingWebhook.swift in Sources */,
269B47671D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
26EC14CA1D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C21D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */,
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */,
);
@@ -467,21 +615,47 @@
files = (
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */,
263993B71CE90EED004A6E93 /* Channel.swift in Sources */,
269B47D01D3C22FC0042D137 /* ClientOptions.swift in Sources */,
26EC14D11D1EF17400FD3A53 /* CustomProfileField.swift in Sources */,
263993B81CE90EED004A6E93 /* User.swift in Sources */,
263993B91CE90EED004A6E93 /* Types.swift in Sources */,
26EC14DF1D1EF17E00FD3A53 /* Item.swift in Sources */,
2678B5981D3151C900CE521A /* Response.swift in Sources */,
269B475C1D3493DE0042D137 /* OAuthResponse.swift in Sources */,
26EC14CE1D1EF17400FD3A53 /* CustomProfile.swift in Sources */,
269B47C91D3AE2620042D137 /* Server.swift in Sources */,
263993BA1CE90EED004A6E93 /* Client.swift in Sources */,
263993BB1CE90EED004A6E93 /* Event.swift in Sources */,
269B47CA1D3AE2670042D137 /* WebhookServer.swift in Sources */,
26EC14D41D1EF17400FD3A53 /* DoNotDisturbStatus.swift in Sources */,
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */,
26EC14E81D1EF18F00FD3A53 /* TeamIcon.swift in Sources */,
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */,
263993BE1CE90EED004A6E93 /* File.swift in Sources */,
263993BF1CE90EED004A6E93 /* SlackWebAPI.swift in Sources */,
26B30BB81D2BC2E4004D4AB5 /* Scope.swift in Sources */,
269B47601D3538E90042D137 /* SlackKit.swift in Sources */,
2678B5991D3151CD00CE521A /* WebhookRequest.swift in Sources */,
26EC14FB1D1F355A00FD3A53 /* Action.swift in Sources */,
26EC14D71D1EF17400FD3A53 /* Edited.swift in Sources */,
263993BF1CE90EED004A6E93 /* WebAPI.swift in Sources */,
2678B5961D3151B900CE521A /* AuthorizeResponse.swift in Sources */,
26EC14E31D1EF18700FD3A53 /* Reaction.swift in Sources */,
26EC14EB1D1EF18F00FD3A53 /* Topic.swift in Sources */,
26B30BC51D2DFF0D004D4AB5 /* OAuthServer.swift in Sources */,
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */,
263993C11CE90EED004A6E93 /* Message.swift in Sources */,
263993C21CE90EED004A6E93 /* Team.swift in Sources */,
269B47681D39AAA80042D137 /* MessageActionResponder.swift in Sources */,
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */,
2678B5971D3151C600CE521A /* MessageActionRequest.swift in Sources */,
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */,
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
269B47C81D3AE25B0042D137 /* MessageActionServer.swift in Sources */,
263993C51CE90EED004A6E93 /* SlackError.swift in Sources */,
26EC14DC1D1EF17E00FD3A53 /* History.swift in Sources */,
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */,
26B30BC91D2DFF7D004D4AB5 /* AuthorizeRequest.swift in Sources */,
269B47641D3544240042D137 /* IncomingWebhook.swift in Sources */,
26EC14CB1D1EF17400FD3A53 /* Comment.swift in Sources */,
26EC14C31D1EF16500FD3A53 /* AttachmentField.swift in Sources */,
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */,
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */,
);
@@ -489,44 +663,7 @@
};
/* 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 = {
@@ -617,6 +754,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -642,6 +780,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -666,6 +805,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -674,6 +814,7 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
@@ -694,6 +835,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -702,6 +844,7 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
@@ -775,15 +918,6 @@
/* 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 = (
+105
View File
@@ -0,0 +1,105 @@
//
// Action.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Action {
public let name: String?
public let text: String?
public let type: String?
public let value: String?
public let style: ActionStyle?
public let confirm: Confirm?
internal init(action:[String: AnyObject]?) {
name = action?["name"] as? String
text = action?["text"] as? String
type = action?["type"] as? String
value = action?["value"] as? String
style = ActionStyle(rawValue: action?["style"] as? String ?? "")
confirm = Confirm(confirm:action?["confirm"] as? [String: AnyObject])
}
public init(name: String, text: String, style: ActionStyle = .Default, value: String? = nil, confirm: Confirm? = nil) {
self.type = "button"
self.name = name
self.text = text
self.value = value
self.style = style
self.confirm = confirm
}
internal func dictionary() -> [String: AnyObject] {
var dict = [String: AnyObject]()
dict["name"] = name
dict["text"] = text
dict["type"] = type
dict["value"] = value
dict["style"] = style?.rawValue
dict["confirm"] = confirm?.dictionary()
return dict
}
public struct Confirm {
public let title: String?
public let text: String?
public let okText: String?
public let dismissText: String?
internal init(confirm:[String: AnyObject]?) {
title = confirm?["title"] as? String
text = confirm?["text"] as? String
okText = confirm?["ok_text"] as? String
dismissText = confirm?["dismiss_text"] as? String
}
public init(text: String, title: String? = nil, okText: String? = nil, dismissText: String? = nil) {
self.text = text
self.title = title
self.okText = okText
self.dismissText = dismissText
}
internal func dictionary() -> [String: AnyObject] {
var dict = [String: AnyObject]()
dict["title"] = title
dict["text"] = text
dict["ok_text"] = okText
dict["dismiss_text"] = dismissText
return dict
}
}
}
public enum ActionStyle: String {
case Default = "default"
case Primary = "primary"
case Danger = "danger"
}
public enum ResponseType: String {
case InChannel = "in_channel"
case Ephemeral = "ephemeral"
}
+14 -42
View File
@@ -21,11 +21,11 @@
// 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 callbackID: String?
public let type: String?
public let color: String?
public let pretext: String?
public let authorName: String?
@@ -35,6 +35,7 @@ public struct Attachment {
public let titleLink: String?
public let text: String?
public let fields: [AttachmentField]?
public let actions: [Action]?
public let imageURL: String?
public let thumbURL: String?
public let footer: String?
@@ -43,6 +44,8 @@ public struct Attachment {
internal init(attachment: [String: AnyObject]?) {
fallback = attachment?["fallback"] as? String
callbackID = attachment?["callback_id"] as? String
type = attachment?["attachment_type"] as? String
color = attachment?["color"] as? String
pretext = attachment?["pretext"] as? String
authorName = attachment?["author_name"] as? String
@@ -57,10 +60,13 @@ public struct Attachment {
footerIcon = attachment?["footer_icon"] as? String
ts = attachment?["ts"] as? Int
fields = (attachment?["fields"] as? [[String: AnyObject]])?.map { AttachmentField(field: $0) }
actions = (attachment?["actions"] as? [[String: AnyObject]])?.map { Action(action: $0) }
}
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, footer: String? = nil, footerIcon:String? = nil, ts:Int? = nil) {
public init(fallback: String, title:String, callbackID: String? = nil, type: String? = nil, colorHex: String? = nil, pretext: String? = nil, authorName: String? = nil, authorLink: String? = nil, authorIcon: String? = nil, titleLink: String? = nil, text: String? = nil, fields: [AttachmentField]? = nil, actions: [Action]? = nil, imageURL: String? = nil, thumbURL: String? = nil, footer: String? = nil, footerIcon:String? = nil, ts:Int? = nil) {
self.fallback = fallback
self.callbackID = callbackID
self.type = type
self.color = colorHex
self.pretext = pretext
self.authorName = authorName
@@ -70,6 +76,7 @@ public struct Attachment {
self.titleLink = titleLink
self.text = text
self.fields = fields
self.actions = actions
self.imageURL = imageURL
self.thumbURL = thumbURL
self.footer = footer
@@ -80,6 +87,8 @@ public struct Attachment {
internal func dictionary() -> [String: AnyObject] {
var attachment = [String: AnyObject]()
attachment["fallback"] = fallback
attachment["callback_id"] = callbackID
attachment["attachment_type"] = type
attachment["color"] = color
attachment["pretext"] = pretext
attachment["authorName"] = authorName
@@ -88,7 +97,8 @@ public struct Attachment {
attachment["title"] = title
attachment["title_link"] = titleLink
attachment["text"] = text
attachment["fields"] = fieldJSONArray(fields)
attachment["fields"] = fields?.map{$0.dictionary()}
attachment["actions"] = actions?.map{$0.dictionary()}
attachment["image_url"] = imageURL
attachment["thumb_url"] = thumbURL
attachment["footer"] = footer
@@ -97,44 +107,6 @@ public struct Attachment {
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 {
+50
View File
@@ -0,0 +1,50 @@
//
// AttachmentField.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct 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
}
}
+48
View File
@@ -0,0 +1,48 @@
//
// AuthorizeRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct AuthorizeRequest {
let clientID: String
let scope: [Scope]
let redirectURI: String
let state: String
let team: String?
var parameters: [String: AnyObject] {
var json = [String : AnyObject]()
json["scope"] = scope.map({$0.rawValue}).joinWithSeparator(",")
json["state"] = state
json["team"] = team
return json
}
init(clientID: String, scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil) {
self.clientID = clientID
self.scope = scope
self.redirectURI = redirectURI
self.state = state
self.team = team
}
}
+36
View File
@@ -0,0 +1,36 @@
//
// AuthorizeResponse.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct AuthorizeResponse {
let code: String
let state: String
init?(queryParameters: [(String, String)]) {
guard let code = queryParameters.first?.1, state = queryParameters.last?.1 else {
return nil
}
self.code = code
self.state = state
}
}
+6
View File
@@ -24,6 +24,7 @@
public struct Bot {
public let id: String?
internal(set) public var botToken: String?
internal(set) public var name: String?
internal(set) public var icons: [String: AnyObject]?
@@ -33,4 +34,9 @@ public struct Bot {
icons = bot?["icons"] as? [String: AnyObject]
}
internal init(botUser: [String: AnyObject]?) {
id = botUser?["bot_user_id"] as? String
botToken = botUser?["bot_access_token"] as? String
}
}
+3 -3
View File
@@ -38,10 +38,10 @@ public struct Channel {
internal(set) public var topic: Topic?
internal(set) public var purpose: Topic?
internal(set) public var isMember: Bool?
internal(set) public var lastRead: String?
public var lastRead: String?
internal(set) public var latest: Message?
internal(set) public var unread: Int?
internal(set) public var unreadCountDisplay: Int?
public var unread: Int?
public var unreadCountDisplay: Int?
internal(set) public var hasPins: Bool?
internal(set) public var members: [String]?
// Client use
@@ -31,7 +31,7 @@ internal extension Client {
switch type {
case .Hello:
connected = true
slackEventsDelegate?.clientConnected()
connectionEventsDelegate?.clientConnected(self)
case .Ok:
messageSent(event)
case .Message:
@@ -144,7 +144,7 @@ internal extension Client {
// Other clients should ignore this event.
break
case .TeamMigrationStarted:
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
connect(options: options ?? ClientOptions())
case .ReconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
+45 -45
View File
@@ -39,7 +39,7 @@ internal extension Client {
message.ts = event.ts
message.text = event.text
channels[channel]?.messages[ts] = message
messageEventsDelegate?.messageSent(message)
messageEventsDelegate?.messageSent(self, message: message)
}
func messageReceived(event: Event) {
@@ -48,7 +48,7 @@ internal extension Client {
}
channels[id]?.messages[ts] = message
messageEventsDelegate?.messageReceived(message)
messageEventsDelegate?.messageReceived(self, message: message)
}
func messageChanged(event: Event) {
@@ -57,7 +57,7 @@ internal extension Client {
}
channels[id]?.messages[ts] = nested
messageEventsDelegate?.messageChanged(nested)
messageEventsDelegate?.messageChanged(self, message: nested)
}
func messageDeleted(event: Event) {
@@ -66,7 +66,7 @@ internal extension Client {
}
channels[id]?.messages.removeValueForKey(key)
messageEventsDelegate?.messageDeleted(message)
messageEventsDelegate?.messageDeleted(self, message: message)
}
//MARK: - Channels
@@ -77,7 +77,7 @@ internal extension Client {
}
channels[channelID]?.usersTyping.append(userID)
channelEventsDelegate?.userTyping(channel, user: user)
channelEventsDelegate?.userTyping(self, channel: channel, user: user)
let timeout = dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC)))
dispatch_after(timeout, dispatch_get_main_queue()) {
@@ -93,7 +93,7 @@ internal extension Client {
}
channels[id]?.lastRead = event.ts
channelEventsDelegate?.channelMarked(channel, timestamp: timestamp)
channelEventsDelegate?.channelMarked(self, channel: channel, timestamp: timestamp)
}
func channelCreated(event: Event) {
@@ -102,7 +102,7 @@ internal extension Client {
}
channels[id] = channel
channelEventsDelegate?.channelCreated(channel)
channelEventsDelegate?.channelCreated(self, channel: channel)
}
func channelDeleted(event: Event) {
@@ -111,7 +111,7 @@ internal extension Client {
}
channels.removeValueForKey(id)
channelEventsDelegate?.channelDeleted(channel)
channelEventsDelegate?.channelDeleted(self, channel: channel)
}
func channelJoined(event: Event) {
@@ -120,7 +120,7 @@ internal extension Client {
}
channels[id] = event.channel
channelEventsDelegate?.channelJoined(channel)
channelEventsDelegate?.channelJoined(self, channel: channel)
}
func channelLeft(event: Event) {
@@ -131,7 +131,7 @@ internal extension Client {
if let userID = authenticatedUser?.id, index = channels[id]?.members?.indexOf(userID) {
channels[id]?.members?.removeAtIndex(index)
}
channelEventsDelegate?.channelLeft(channel)
channelEventsDelegate?.channelLeft(self, channel: channel)
}
func channelRenamed(event: Event) {
@@ -140,7 +140,7 @@ internal extension Client {
}
channels[id]?.name = channel.name
channelEventsDelegate?.channelRenamed(channel)
channelEventsDelegate?.channelRenamed(self, channel: channel)
}
func channelArchived(event: Event, archived: Bool) {
@@ -149,14 +149,14 @@ internal extension Client {
}
channels[id]?.isArchived = archived
channelEventsDelegate?.channelArchived(channel)
channelEventsDelegate?.channelArchived(self, channel: channel)
}
func channelHistoryChanged(event: Event) {
guard let channel = event.channel else {
return
}
channelEventsDelegate?.channelHistoryChanged(channel)
channelEventsDelegate?.channelHistoryChanged(self, channel: channel)
}
//MARK: - Do Not Disturb
@@ -166,7 +166,7 @@ internal extension Client {
}
authenticatedUser?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUpdated(dndStatus)
doNotDisturbEventsDelegate?.doNotDisturbUpdated(self, dndStatus: dndStatus)
}
func doNotDisturbUserUpdated(event: Event) {
@@ -175,7 +175,7 @@ internal extension Client {
}
users[id]?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUserUpdated(dndStatus, user: user)
doNotDisturbEventsDelegate?.doNotDisturbUserUpdated(self, dndStatus: dndStatus, user: user)
}
//MARK: - IM & Group Open/Close
@@ -185,7 +185,7 @@ internal extension Client {
}
channels[id]?.isOpen = open
groupEventsDelegate?.groupOpened(channel)
groupEventsDelegate?.groupOpened(self, group: channel)
}
//MARK: - Files
@@ -200,7 +200,7 @@ internal extension Client {
}
files[id] = file
fileEventsDelegate?.fileProcessed(file)
fileEventsDelegate?.fileProcessed(self, file: file)
}
func filePrivate(event: Event) {
@@ -209,7 +209,7 @@ internal extension Client {
}
files[id]?.isPublic = false
fileEventsDelegate?.fileMadePrivate(file)
fileEventsDelegate?.fileMadePrivate(self, file: file)
}
func deleteFile(event: Event) {
@@ -220,7 +220,7 @@ internal extension Client {
if files[id] != nil {
files.removeValueForKey(id)
}
fileEventsDelegate?.fileDeleted(file)
fileEventsDelegate?.fileDeleted(self, file: file)
}
func fileCommentAdded(event: Event) {
@@ -229,7 +229,7 @@ internal extension Client {
}
files[id]?.comments[commentID] = comment
fileEventsDelegate?.fileCommentAdded(file, comment: comment)
fileEventsDelegate?.fileCommentAdded(self, file: file, comment: comment)
}
func fileCommentEdited(event: Event) {
@@ -238,7 +238,7 @@ internal extension Client {
}
files[id]?.comments[commentID]?.comment = comment.comment
fileEventsDelegate?.fileCommentEdited(file, comment: comment)
fileEventsDelegate?.fileCommentEdited(self, file: file, comment: comment)
}
func fileCommentDeleted(event: Event) {
@@ -247,7 +247,7 @@ internal extension Client {
}
files[id]?.comments.removeValueForKey(commentID)
fileEventsDelegate?.fileCommentDeleted(file, comment: comment)
fileEventsDelegate?.fileCommentDeleted(self, file: file, comment: comment)
}
//MARK: - Pins
@@ -257,7 +257,7 @@ internal extension Client {
}
channels[id]?.pinnedItems.append(item)
pinEventsDelegate?.itemPinned(item, channel: channels[id])
pinEventsDelegate?.itemPinned(self, item: item, channel: channels[id])
}
func pinRemoved(event: Event) {
@@ -268,7 +268,7 @@ internal extension Client {
if let pins = channels[id]?.pinnedItems.filter({$0 != item}) {
channels[id]?.pinnedItems = pins
}
pinEventsDelegate?.itemUnpinned(item, channel: channels[id])
pinEventsDelegate?.itemUnpinned(self, item: item, channel: channels[id])
}
//MARK: - Stars
@@ -287,7 +287,7 @@ internal extension Client {
break
}
starEventsDelegate?.itemStarred(item, star: star)
starEventsDelegate?.itemStarred(self, item: item, star: star)
}
func starMessage(item: Item, star: Bool) {
@@ -347,7 +347,7 @@ internal extension Client {
break
}
reactionEventsDelegate?.reactionAdded(reaction, item: item, itemUser: itemUser)
reactionEventsDelegate?.reactionAdded(self, reaction: reaction, item: item, itemUser: itemUser)
}
func removedReaction(event: Event) {
@@ -375,7 +375,7 @@ internal extension Client {
break
}
reactionEventsDelegate?.reactionRemoved(key, item: item, itemUser: itemUser)
reactionEventsDelegate?.reactionRemoved(self, reaction: key, item: item, itemUser: itemUser)
}
//MARK: - Preferences
@@ -385,7 +385,7 @@ internal extension Client {
}
authenticatedUser?.preferences?[name] = event.value
slackEventsDelegate?.preferenceChanged(name, value: event.value)
slackEventsDelegate?.preferenceChanged(self, preference: name, value: event.value)
}
//Mark: - User Change
@@ -397,7 +397,7 @@ internal extension Client {
let preferences = users[id]?.preferences
users[id] = user
users[id]?.preferences = preferences
slackEventsDelegate?.userChanged(user)
slackEventsDelegate?.userChanged(self, user: user)
}
//MARK: - User Presence
@@ -407,7 +407,7 @@ internal extension Client {
}
users[id]?.presence = event.presence
slackEventsDelegate?.presenceChanged(user, presence: presence)
slackEventsDelegate?.presenceChanged(self, user: user, presence: presence)
}
//MARK: - Team
@@ -417,7 +417,7 @@ internal extension Client {
}
users[id] = user
teamEventsDelegate?.teamJoined(user)
teamEventsDelegate?.teamJoined(self, user: user)
}
func teamPlanChange(event: Event) {
@@ -426,7 +426,7 @@ internal extension Client {
}
team?.plan = plan
teamEventsDelegate?.teamPlanChanged(plan)
teamEventsDelegate?.teamPlanChanged(self, plan: plan)
}
func teamPreferenceChange(event: Event) {
@@ -435,7 +435,7 @@ internal extension Client {
}
team?.prefs?[name] = event.value
teamEventsDelegate?.teamPreferencesChanged(name, value: event.value)
teamEventsDelegate?.teamPreferencesChanged(self, preference: name, value: event.value)
}
func teamNameChange(event: Event) {
@@ -444,7 +444,7 @@ internal extension Client {
}
team?.name = name
teamEventsDelegate?.teamNameChanged(name)
teamEventsDelegate?.teamNameChanged(self, name: name)
}
func teamDomainChange(event: Event) {
@@ -453,7 +453,7 @@ internal extension Client {
}
team?.domain = domain
teamEventsDelegate?.teamDomainChanged(domain)
teamEventsDelegate?.teamDomainChanged(self, domain: domain)
}
func emailDomainChange(event: Event) {
@@ -462,11 +462,11 @@ internal extension Client {
}
team?.emailDomain = domain
teamEventsDelegate?.teamEmailDomainChanged(domain)
teamEventsDelegate?.teamEmailDomainChanged(self, domain: domain)
}
func emojiChanged(event: Event) {
teamEventsDelegate?.teamEmojiChanged()
teamEventsDelegate?.teamEmojiChanged(self)
}
//MARK: - Bots
@@ -476,7 +476,7 @@ internal extension Client {
}
bots[id] = bot
slackEventsDelegate?.botEvent(bot)
slackEventsDelegate?.botEvent(self, bot: bot)
}
//MARK: - Subteams
@@ -486,7 +486,7 @@ internal extension Client {
}
userGroups[id] = subteam
subteamEventsDelegate?.subteamEvent(subteam)
subteamEventsDelegate?.subteamEvent(self, userGroup: subteam)
}
func subteamAddedSelf(event: Event) {
@@ -495,7 +495,7 @@ internal extension Client {
}
authenticatedUser?.userGroups![subteamID] = subteamID
subteamEventsDelegate?.subteamSelfAdded(subteamID)
subteamEventsDelegate?.subteamSelfAdded(self, subteamID: subteamID)
}
func subteamRemovedSelf(event: Event) {
@@ -504,7 +504,7 @@ internal extension Client {
}
authenticatedUser?.userGroups?.removeValueForKey(subteamID)
subteamEventsDelegate?.subteamSelfRemoved(subteamID)
subteamEventsDelegate?.subteamSelfRemoved(self, subteamID: subteamID)
}
//MARK: - Team Profiles
@@ -519,7 +519,7 @@ internal extension Client {
}
}
teamProfileEventsDelegate?.teamProfileChanged(profile)
teamProfileEventsDelegate?.teamProfileChanged(self, profile: profile)
}
func teamProfileDeleted(event: Event) {
@@ -533,7 +533,7 @@ internal extension Client {
}
}
teamProfileEventsDelegate?.teamProfileDeleted(profile)
teamProfileEventsDelegate?.teamProfileDeleted(self, profile: profile)
}
func teamProfileReordered(event: Event) {
@@ -547,7 +547,7 @@ internal extension Client {
}
}
teamProfileEventsDelegate?.teamProfileReordered(profile)
teamProfileEventsDelegate?.teamProfileReordered(self, profile: profile)
}
//MARK: - Authenticated User
@@ -557,7 +557,7 @@ internal extension Client {
}
authenticatedUser?.presence = presence
slackEventsDelegate?.manualPresenceChanged(user, presence: presence)
slackEventsDelegate?.manualPresenceChanged(self, user: user, presence: presence)
}
}
-2
View File
@@ -21,8 +21,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public enum ClientError: ErrorType {
case ChannelDoesNotExist
case UserDoesNotExist
+34 -39
View File
@@ -24,7 +24,7 @@
import Foundation
import Starscream
public class Client: WebSocketDelegate {
public final class Client: WebSocketDelegate {
internal(set) public var connected = false
internal(set) public var authenticatedUser: User?
@@ -37,7 +37,20 @@ public class Client: WebSocketDelegate {
internal(set) public var files = [String: File]()
internal(set) public var sentMessages = [String: Message]()
public var token = "xoxp-SLACK_AUTH_TOKEN"
public var webAPI: WebAPI {
return WebAPI(token: token)
}
internal var webSocket: WebSocket?
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
internal var ping: Double?
internal var pong: Double?
internal var options: ClientOptions?
//MARK: - Delegates
public weak var connectionEventsDelegate: ConnectionEventsDelegate?
public weak var slackEventsDelegate: SlackEventsDelegate?
public weak var messageEventsDelegate: MessageEventsDelegate?
public weak var doNotDisturbEventsDelegate: DoNotDisturbEventsDelegate?
@@ -50,47 +63,29 @@ public class Client: WebSocketDelegate {
public weak var teamEventsDelegate: TeamEventsDelegate?
public weak var subteamEventsDelegate: SubteamEventsDelegate?
public weak var teamProfileEventsDelegate: TeamProfileEventsDelegate?
public var token = "SLACK_AUTH_TOKEN"
// If you already have an API token
internal init(apiToken: String) {
self.token = apiToken
}
public func setAuthToken(token: String) {
self.token = token
}
public var webAPI: SlackWebAPI {
return SlackWebAPI(client: self)
}
internal var webSocket: WebSocket?
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(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: {
public func connect(options options: ClientOptions = ClientOptions()) {
self.options = options
webAPI.rtmStart(options.simpleLatest, noUnreads: options.noUnreads, mpimAware: options.mpimAware, success: {
(response) -> Void in
self.initialSetup(response)
if let socketURL = response["url"] as? String {
let url = NSURL(string: socketURL)
self.webSocket = WebSocket(url: url!)
self.webSocket?.delegate = self
self.webSocket?.connect()
guard let socketURL = response["url"] as? String, url = NSURL(string: socketURL) else {
return
}
self.initialSetup(response)
self.webSocket = WebSocket(url: url)
self.webSocket?.delegate = self
self.webSocket?.connect()
}, failure: {(error) -> Void in
self.slackEventsDelegate?.clientConnectionFailed(error)
self.connectionEventsDelegate?.clientConnectionFailed(self, error: error)
})
}
@@ -163,7 +158,7 @@ public class Client: WebSocketDelegate {
}
private func timeoutCheck() -> Bool {
if let pong = pong, ping = ping, timeout = timeout {
if let pong = pong, ping = ping, timeout = options?.timeout {
if pong - ping < timeout {
return true
} else {
@@ -240,7 +235,7 @@ public class Client: WebSocketDelegate {
// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = pingInterval {
if let pingInterval = options?.pingInterval {
pingRTMServerAtInterval(pingInterval)
}
}
@@ -249,9 +244,9 @@ public class Client: WebSocketDelegate {
connected = false
webSocket = nil
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
connectionEventsDelegate?.clientDisconnected(self)
if let options = options where options.reconnect == true {
connect(options: options)
}
}
+44
View File
@@ -0,0 +1,44 @@
//
// ClientOptions.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct ClientOptions {
let simpleLatest: Bool?
let noUnreads: Bool?
let mpimAware: Bool?
let pingInterval: NSTimeInterval?
let timeout: NSTimeInterval?
let reconnect: Bool?
public init(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
self.simpleLatest = simpleLatest
self.noUnreads = noUnreads
self.mpimAware = mpimAware
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
}
}
+52
View File
@@ -0,0 +1,52 @@
//
// Comment.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Comment {
public let id: String?
public let user: String?
internal(set) public var created: Int?
internal(set) public var comment: String?
internal(set) public var starred: Bool?
internal(set) public var stars: Int?
internal(set) public var reactions = [Reaction]()
internal init(comment:[String: AnyObject]?) {
id = comment?["id"] as? String
created = comment?["created"] as? Int
user = comment?["user"] as? String
starred = comment?["is_starred"] as? Bool
stars = comment?["num_stars"] as? Int
self.comment = comment?["comment"] as? String
}
internal init(id: String?) {
self.id = id
self.user = nil
}
}
extension Comment: Equatable {}
public func ==(lhs: Comment, rhs: Comment) -> Bool {
return lhs.id == rhs.id
}
+50
View File
@@ -0,0 +1,50 @@
//
// CustomProfile.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct CustomProfile {
internal(set) public var fields = [String: CustomProfileField]()
internal init(profile: [String: AnyObject]?) {
if let eventFields = profile?["fields"] as? [AnyObject] {
for field in eventFields {
var cpf: CustomProfileField?
if let fieldDictionary = field as? [String: AnyObject] {
cpf = CustomProfileField(field: fieldDictionary)
} else {
cpf = CustomProfileField(id: field as? String)
}
if let id = cpf?.id { fields[id] = cpf }
}
}
}
internal init(customFields: [String: AnyObject]?) {
if let customFields = customFields {
for key in customFields.keys {
let cpf = CustomProfileField(field: customFields[key] as? [String: AnyObject])
self.fields[key] = cpf
}
}
}
}
+65
View File
@@ -0,0 +1,65 @@
//
// CustomProfileField.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct 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
}
}
+39
View File
@@ -0,0 +1,39 @@
//
// DoNotDisturbStatus.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct DoNotDisturbStatus {
internal(set) public var enabled: Bool?
internal(set) public var nextDoNotDisturbStart: Int?
internal(set) public var nextDoNotDisturbEnd: Int?
internal(set) public var snoozeEnabled: Bool?
internal(set) public var snoozeEndtime: Int?
internal init(status: [String: AnyObject]?) {
enabled = status?["dnd_enabled"] as? Bool
nextDoNotDisturbStart = status?["next_dnd_start_ts"] as? Int
nextDoNotDisturbEnd = status?["next_dnd_end_ts"] as? Int
snoozeEnabled = status?["snooze_enabled"] as? Bool
snoozeEndtime = status?["snooze_endtime"] as? Int
}
}
+32
View File
@@ -0,0 +1,32 @@
//
// Edited.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Edited {
public let user: String?
public let ts: String?
internal init(edited:[String: AnyObject]?) {
user = edited?["user"] as? String
ts = edited?["ts"] as? String
}
}
+1 -5
View File
@@ -161,11 +161,7 @@ internal struct Event {
var profile: CustomProfile?
init(event:[String: AnyObject]) {
if let eventType = event["type"] as? String {
type = EventType(rawValue:eventType)
} else {
type = EventType(rawValue: "ok")
}
type = EventType(rawValue: event["type"] as? String ?? "ok")
ts = event["ts"] as? String
subtype = event["subtype"] as? String
channelID = event["channel_id"] as? String
+52 -51
View File
@@ -21,88 +21,89 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public protocol SlackEventsDelegate: class {
func clientConnectionFailed(error: SlackError)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject?)
func userChanged(user: User)
func presenceChanged(user: User, presence: String)
func manualPresenceChanged(user: User, presence: String)
func botEvent(bot: Bot)
public protocol ConnectionEventsDelegate: class {
func clientConnected(client: Client)
func clientDisconnected(client: Client)
func clientConnectionFailed(client: Client, error: SlackError)
}
public protocol MessageEventsDelegate: class {
func messageSent(message: Message)
func messageReceived(message: Message)
func messageChanged(message: Message)
func messageDeleted(message: Message?)
func messageSent(client: Client, message: Message)
func messageReceived(client: Client, message: Message)
func messageChanged(client: Client, message: Message)
func messageDeleted(client: Client, message: Message?)
}
public protocol ChannelEventsDelegate: class {
func userTyping(channel: Channel, user: User)
func channelMarked(channel: Channel, timestamp: String)
func channelCreated(channel: Channel)
func channelDeleted(channel: Channel)
func channelRenamed(channel: Channel)
func channelArchived(channel: Channel)
func channelHistoryChanged(channel: Channel)
func channelJoined(channel: Channel)
func channelLeft(channel: Channel)
func userTyping(client: Client, channel: Channel, user: User)
func channelMarked(client: Client, channel: Channel, timestamp: String)
func channelCreated(client: Client, channel: Channel)
func channelDeleted(client: Client, channel: Channel)
func channelRenamed(client: Client, channel: Channel)
func channelArchived(client: Client, channel: Channel)
func channelHistoryChanged(client: Client, channel: Channel)
func channelJoined(client: Client, channel: Channel)
func channelLeft(client: Client, channel: Channel)
}
public protocol DoNotDisturbEventsDelegate: class {
func doNotDisturbUpdated(dndStatus: DoNotDisturbStatus)
func doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User)
func doNotDisturbUpdated(client: Client, dndStatus: DoNotDisturbStatus)
func doNotDisturbUserUpdated(client: Client, dndStatus: DoNotDisturbStatus, user: User)
}
public protocol GroupEventsDelegate: class {
func groupOpened(group: Channel)
func groupOpened(client: Client, group: Channel)
}
public protocol FileEventsDelegate: class {
func fileProcessed(file: File)
func fileMadePrivate(file: File)
func fileDeleted(file: File)
func fileCommentAdded(file: File, comment: Comment)
func fileCommentEdited(file: File, comment: Comment)
func fileCommentDeleted(file: File, comment: Comment)
func fileProcessed(client: Client, file: File)
func fileMadePrivate(client: Client, file: File)
func fileDeleted(client: Client, file: File)
func fileCommentAdded(client: Client, file: File, comment: Comment)
func fileCommentEdited(client: Client, file: File, comment: Comment)
func fileCommentDeleted(client: Client, file: File, comment: Comment)
}
public protocol PinEventsDelegate: class {
func itemPinned(item: Item, channel: Channel?)
func itemUnpinned(item: Item, channel: Channel?)
func itemPinned(client: Client, item: Item, channel: Channel?)
func itemUnpinned(client: Client, item: Item, channel: Channel?)
}
public protocol StarEventsDelegate: class {
func itemStarred(item: Item, star: Bool)
func itemStarred(client: Client, item: Item, star: Bool)
}
public protocol ReactionEventsDelegate: class {
func reactionAdded(reaction: String, item: Item, itemUser: String)
func reactionRemoved(reaction: String, item: Item, itemUser: String)
func reactionAdded(client: Client, reaction: String, item: Item, itemUser: String)
func reactionRemoved(client: Client, reaction: String, item: Item, itemUser: String)
}
public protocol SlackEventsDelegate: class {
func preferenceChanged(client: Client, preference: String, value: AnyObject?)
func userChanged(client: Client, user: User)
func presenceChanged(client: Client, user: User, presence: String)
func manualPresenceChanged(client: Client, user: User, presence: String)
func botEvent(client: Client, bot: Bot)
}
public protocol TeamEventsDelegate: class {
func teamJoined(user: User)
func teamPlanChanged(plan: String)
func teamPreferencesChanged(preference: String, value: AnyObject?)
func teamNameChanged(name: String)
func teamDomainChanged(domain: String)
func teamEmailDomainChanged(domain: String)
func teamEmojiChanged()
func teamJoined(client: Client, user: User)
func teamPlanChanged(client: Client, plan: String)
func teamPreferencesChanged(client: Client, preference: String, value: AnyObject?)
func teamNameChanged(client: Client, name: String)
func teamDomainChanged(client: Client, domain: String)
func teamEmailDomainChanged(client: Client, domain: String)
func teamEmojiChanged(client: Client)
}
public protocol SubteamEventsDelegate: class {
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
func subteamEvent(client: Client, userGroup: UserGroup)
func subteamSelfAdded(client: Client, subteamID: String)
func subteamSelfRemoved(client: Client, subteamID: String)
}
public protocol TeamProfileEventsDelegate: class {
func teamProfileChanged(profile: CustomProfile)
func teamProfileDeleted(profile: CustomProfile)
func teamProfileReordered(profile: CustomProfile)
func teamProfileChanged(client: Client, profile: CustomProfile)
func teamProfileDeleted(client: Client, profile: CustomProfile)
func teamProfileReordered(client: Client, profile: CustomProfile)
}
+18
View File
@@ -41,3 +41,21 @@ internal extension String {
}
}
internal extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
var requestStringFromParameters: String {
var requestString = ""
for key in self.keys {
if let value = self[key] as? String, encodedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet()) {
requestString += "&\(key)=\(encodedValue)"
} else if let value = self[key] as? Int {
requestString += "&\(key)=\(value)"
}
}
return requestString
}
}
+43
View File
@@ -0,0 +1,43 @@
//
// History.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 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 {
messages.append(Message(message: message))
}
}
hasMore = history?["has_more"] as? Bool
}
}
+72
View File
@@ -0,0 +1,72 @@
//
// IncomingWebhook.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public struct IncomingWebhook {
public let url: String?
public let channel: String?
public let configurationURL: String?
public let username: String?
public let iconEmoji: String?
public let iconURL: String?
internal init(webhook: [String: AnyObject]?) {
url = webhook?["url"] as? String
channel = webhook?["channel"] as? String
configurationURL = webhook?["configuration_url"] as? String
username = webhook?["username"] as? String
iconEmoji = webhook?["icon_emoji"] as? String
iconURL = webhook?["icon_url"] as? String
}
public init(url: String, channel: String? = nil, username: String? = nil, iconEmoji: String? = nil, iconURL: String? = nil) {
self.url = url
self.channel = channel
self.username = username
self.iconEmoji = iconEmoji
self.iconURL = iconURL
self.configurationURL = nil
}
public func postMessage(response: Response, success: ((Bool)->Void)? = nil, failure: ((SlackError)->Void)? = nil) {
if let url = self.url, data = try? NSJSONSerialization.dataWithJSONObject(jsonBody(response.json()), options: []) {
NetworkInterface().customRequest(url, data: data, success: { _ in
success?(true)
}, errorClosure: {(error) in
failure?(error)
})
}
}
private func jsonBody(response: [String: AnyObject]) -> [String: AnyObject] {
var json = response
json["channel"] = channel
json["username"] = username
json["icon_emoji"] = iconEmoji
json["icon_url"] = iconURL
return json
}
}
+61
View File
@@ -0,0 +1,61 @@
//
// Item.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Item {
public let type: String?
public let ts: String?
public let channel: String?
public let message: Message?
public let file: File?
public let comment: Comment?
public let fileCommentID: String?
internal init(item:[String: AnyObject]?) {
type = item?["type"] as? String
ts = item?["ts"] as? String
channel = item?["channel"] as? String
message = Message(message: item?["message"] as? [String: AnyObject])
// Comment and File can come across as Strings or Dictionaries
if let commentDictionary = item?["comment"] as? [String: AnyObject] {
comment = Comment(comment: commentDictionary)
} else {
comment = Comment(id: item?["comment"] as? String)
}
if let fileDictionary = item?["file"] as? [String: AnyObject] {
file = File(file: fileDictionary)
} else {
file = File(id: item?["file"] as? String)
}
fileCommentID = item?["file_comment"] as? String
}
}
extension Item: Equatable {}
public func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.type == rhs.type && lhs.channel == rhs.channel && lhs.file == rhs.file && lhs.comment == rhs.comment && lhs.message == rhs.message
}
+8 -4
View File
@@ -21,7 +21,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public class Message {
public final class Message {
public let type = "message"
public let subtype: String?
@@ -47,6 +47,9 @@ public class Message {
public let file: File?
internal(set) public var reactions = [Reaction]()
internal(set) public var attachments: [Attachment]?
internal(set) public var responseType: ResponseType?
internal(set) public var replaceOriginal: Bool?
internal(set) public var deleteOriginal: Bool?
public init(message: [String: AnyObject]?) {
subtype = message?["subtype"] as? String
@@ -71,9 +74,10 @@ public class Message {
comment = Comment(comment: message?["comment"] as? [String: AnyObject])
file = File(file: message?["file"] as? [String: AnyObject])
reactions = Reaction.reactionsFromArray(message?["reactions"] as? [[String: AnyObject]])
attachments = (message?["attachments"] as? [[String: AnyObject]])?.map({(attachment) -> Attachment in
return Attachment(attachment: attachment)
})
attachments = (message?["attachments"] as? [[String: AnyObject]])?.map{Attachment(attachment: $0)}
responseType = ResponseType(rawValue: message?["response_type"] as? String ?? "")
replaceOriginal = message?["replace_original"] as? Bool
deleteOriginal = message?["delete_original"] as? Bool
}
internal init(ts:String?) {
@@ -0,0 +1,52 @@
//
// MessageActionRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct MessageActionRequest: Request {
let action: Action?
let callbackID: String?
let team: Team?
let channel: Channel?
let user: User?
let actionTS: String?
let messageTS: String?
let attachmentID: String?
let token: String?
let originalMessage: Message?
let responseURL: String
init(response: [String: AnyObject]?) {
action = (response?["actions"] as? [[String:AnyObject]])?.map({Action(action: $0)}).first
callbackID = response?["callback_id"] as? String
team = Team(team: response?["team"] as? [String: AnyObject])
channel = Channel(channel: response?["channel"] as? [String: AnyObject])
user = User(user: response?["channel"] as? [String: AnyObject])
actionTS = response?["action_ts"] as? String
messageTS = response?["message_ts"] as? String
attachmentID = response?["attachment_id"] as? String
token = response?["token"] as? String
originalMessage = Message(message: response?["original_message"] as? [String: AnyObject])
responseURL = response?["response_url"] as? String ?? ""
}
}
@@ -1,6 +1,5 @@
//
// AppDelegate.swift
// OSX-Sample
// MessageActionResponder.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -22,23 +21,20 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Cocoa
import SlackKit
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
let leaderboard = Leaderboard(token: "SLACK_AUTH_TOKEN")
public struct MessageActionResponder {
func applicationDidFinishLaunching(aNotification: NSNotification) {
leaderboard.client.connect()
public var responses:[(Action, Response)]
public init(responses:[(Action, Response)]) {
self.responses = responses
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
internal func responseForRequest(request:MessageActionRequest) -> Reply? {
if let response = responses.filter({$0.0.name == request.action?.name}).first?.1 {
return Reply.JSON(response: response)
} else {
return nil
}
}
}
@@ -0,0 +1,46 @@
//
// MessageActionServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public class MessageActionServer: Server {
internal let responder: MessageActionResponder
required public init(token: String, route: String, responder: MessageActionResponder) {
self.responder = responder
super.init(token: token)
addRoute(route)
}
internal func addRoute(route: String) {
http.POST["/\(route)"] = { request in
let payload = request.parseUrlencodedForm()
let actionRequest = MessageActionRequest(response: self.jsonFromRequest(payload[0].1))
if let reply = self.responder.responseForRequest(actionRequest) where actionRequest.token == self.token {
return self.request(actionRequest, reply: reply)
} else {
return .BadRequest(.Text("Bad request."))
}
}
}
}
+30 -19
View File
@@ -27,10 +27,13 @@ internal struct NetworkInterface {
private let apiUrl = "https://slack.com/api/"
internal func request(endpoint: SlackAPIEndpoint, token: String, parameters: [String: AnyObject]?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?token=\(token)"
internal func request(endpoint: Endpoint, token: String? = nil, parameters: [String: AnyObject]?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(endpoint.rawValue)?"
if let token = token {
requestString += "token=\(token)"
}
if let params = parameters {
requestString += requestStringFromParameters(params)
requestString += params.requestStringFromParameters
}
guard let url = NSURL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
@@ -47,10 +50,31 @@ internal struct NetworkInterface {
}.resume()
}
internal func customRequest(url: String, data: NSData, success: (Bool)->Void, errorClosure: (SlackError)->Void) {
guard let requestString = url.stringByRemovingPercentEncoding, url = NSURL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
return
}
let request = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
let contentType = "application/json"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.HTTPBody = data
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
if internalError == nil {
success(true)
} else {
errorClosure(SlackError.ClientNetworkError)
}
}.resume()
}
internal func uploadRequest(token: String, data: NSData, parameters: [String: AnyObject]?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
var requestString = "\(apiUrl)\(SlackAPIEndpoint.FilesUpload.rawValue)?token=\(token)"
var requestString = "\(apiUrl)\(Endpoint.FilesUpload.rawValue)?token=\(token)"
if let params = parameters {
requestString = requestString + requestStringFromParameters(params)
requestString = requestString + params.requestStringFromParameters
}
guard let url = NSURL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
@@ -103,7 +127,7 @@ internal struct NetworkInterface {
successClosure(json)
} else {
if let errorString = json["error"] as? String {
throw ErrorDispatcher.dispatch(errorString)
throw SlackError(rawValue: errorString) ?? .UnknownError
} else {
throw SlackError.UnknownError
}
@@ -126,17 +150,4 @@ internal struct NetworkInterface {
return String(format: "slackkit.boundary.%08x%08x", arc4random(), arc4random())
}
private func requestStringFromParameters(parameters: [String: AnyObject]) -> String {
var requestString = ""
for key in parameters.keys {
if let value = parameters[key] as? String, encodedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet()) {
requestString += "&\(key)=\(encodedValue)"
} else if let value = parameters[key] as? Int {
requestString += "&\(key)=\(value)"
}
}
return requestString
}
}
+44
View File
@@ -0,0 +1,44 @@
//
// OAuthResponse.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct OAuthResponse {
let accessToken: String?
let scope: [Scope]?
let userID: String?
let teamName: String?
let teamID: String?
let incomingWebhook: IncomingWebhook?
let bot: Bot?
init(response: [String: AnyObject]?) {
accessToken = response?["access_token"] as? String
scope = (response?["scope"] as? String)?.componentsSeparatedByString(",").flatMap{Scope(rawValue:$0)}
userID = response?["user_id"] as? String
teamName = response?["team_name"] as? String
teamID = response?["team_id"] as? String
incomingWebhook = IncomingWebhook(webhook: response?["incoming_webhook"] as? [String: AnyObject])
bot = Bot(botUser: response?["bot"] as? [String: AnyObject])
}
}
+99
View File
@@ -0,0 +1,99 @@
//
// OAuthServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Swifter
internal protocol OAuthDelegate {
mutating func userAuthed(response: OAuthResponse)
}
public struct OAuthServer {
private let oauthURL = "https://slack.com/oauth/authorize"
private let http = HttpServer()
private let clientID: String
private let clientSecret: String
private let state: String?
private let redirectURI: String?
private var delegate: OAuthDelegate?
internal init(clientID: String, clientSecret: String, state: String? = nil, redirectURI: String? = nil, port:in_port_t = 8080, forceIPV4: Bool = false, delegate: OAuthDelegate? = nil) throws {
self.clientID = clientID
self.clientSecret = clientSecret
self.state = state ?? "state"
self.redirectURI = redirectURI
self.delegate = delegate
oauthRoute()
start(port, forceIPV4: forceIPV4)
}
public func start(port: in_port_t = 8080, forceIPV4: Bool = false) {
do {
try http.start(port, forceIPv4: forceIPV4)
} catch let error as NSError {
print("Server failed to start with error: \(error)")
}
}
public func stop() {
http.stop()
}
private mutating func oauthRoute() {
http["/oauth"] = { request in
guard let response = AuthorizeResponse(queryParameters: request.queryParams) where response.state == self.state else {
return .BadRequest(.Text("Bad request."))
}
self.oauthRequest(response)
if let redirect = self.redirectURI {
return .MovedPermanently(redirect)
}
return .OK(.Text("Authentication successful."))
}
}
private mutating func oauthRequest(auth: AuthorizeResponse) {
WebAPI.oauthAccess(self.clientID, clientSecret: self.clientSecret, code: auth.code, redirectURI: self.redirectURI, success: {(response) in
self.delegate?.userAuthed(OAuthResponse(response: response))
}, failure: {(error) in
})
}
private func oauthURLRequest(authorize: AuthorizeRequest) -> NSURLRequest? {
var requestString = "\(oauthURL)?client_id=\(authorize.clientID)"
requestString += authorize.parameters.requestStringFromParameters
guard let url = NSURL(string: requestString) else {
return nil
}
return NSURLRequest(URL:url)
}
public func authorizeRequest(scope:[Scope], redirectURI: String, state: String = "slackkit", team: String? = nil) -> NSURLRequest? {
let request = AuthorizeRequest(clientID: clientID, scope: scope, redirectURI: redirectURI, state: state, team: team)
return oauthURLRequest(request)
}
}
+57
View File
@@ -0,0 +1,57 @@
//
// Reaction.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Reaction {
public let name: String?
internal(set) public var user: String?
internal init(reaction:[String: AnyObject]?) {
name = reaction?["name"] as? String
}
internal init(name: String, user: String) {
self.name = name
self.user = user
}
static func reactionsFromArray(array: [[String: AnyObject]]?) -> [Reaction] {
var reactions = [Reaction]()
if let array = array {
for reaction in array {
if let users = reaction["users"] as? [String], name = reaction["name"] as? String {
for user in users {
reactions.append(Reaction(name: name, user: user))
}
}
}
}
return reactions
}
}
extension Reaction: Equatable {}
public func ==(lhs: Reaction, rhs: Reaction) -> Bool {
return lhs.name == rhs.name
}
+44
View File
@@ -0,0 +1,44 @@
//
// Response.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Response {
let text: String
let responseType: ResponseType?
let attachments: [Attachment]?
public init(text: String, responseType: ResponseType? = nil, attachments: [Attachment]? = nil) {
self.responseType = responseType
self.text = text
self.attachments = attachments
}
internal func json() -> [String: AnyObject] {
var json = [String : AnyObject]()
json["text"] = text
json["response_type"] = responseType?.rawValue
json["attachments"] = attachments?.map({$0.dictionary()})
return json
}
}
+71
View File
@@ -0,0 +1,71 @@
//
// Scope.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public enum Scope: String {
case ChannelsHistory = "channels:history"
case ChannelsRead = "channels:read"
case ChannelsWrite = "channels:write"
case ChatWriteBot = "chat:write:bot"
case ChatWriteUser = "chat:write:user"
case DNDRead = "dnd:read"
case DNDWrite = "dnd:write"
case EmojiRead = "emoji:read"
case FilesRead = "files:read"
case FilesWriteUser = "files:write:user"
case GroupsHistory = "groups:history"
case GroupsRead = "groups:read"
case GroupsWrite = "groups:write"
case IdentityBasic = "identity.basic"
case IMHistory = "im:history"
case IMRead = "im:read"
case IMWrite = "im:write"
case MPIMHistory = "mpim:history"
case MPIMRead = "mpim:read"
case MPIMWrite = "mpim:write"
case PinsRead = "pins:read"
case PinsWrite = "pins:write"
case ReactionsRead = "reactions:read"
case ReactionsWrite = "reactions:write"
case RemindersRead = "reminders:read"
case RemindersWrite = "reminders:write"
case SearchRead = "search:read"
case StarsRead = "stars:read"
case StarsWrite = "stars:write"
case TeamRead = "team:read"
case UserGroupsRead = "usergroups:read"
case UserGroupsWrite = "usergroups:write"
case UserProfilesRead = "user.profiles:read"
case UserProfilesWrite = "user.profiles:write"
case UsersRead = "users:read"
case UsersWrite = "users:write"
case IncomingWebhook = "incoming-webhook"
case Commands = "commands"
case Bot = "bot"
case Identify = "identify"
case Client = "client"
case Admin = "admin"
//Deprecated
case Read = "read"
case Post = "post"
}
+91
View File
@@ -0,0 +1,91 @@
//
// Server.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Swifter
internal enum Reply {
case JSON(response: Response)
case Text(body: String)
case BadRequest
}
internal protocol Request {
var responseURL: String { get }
}
public class Server {
internal let http = HttpServer()
internal let token: String
internal init(token: String) {
self.token = token
}
public func start(port: in_port_t = 8080, forceIPV4: Bool = false) {
do {
try http.start(port, forceIPv4: forceIPV4)
} catch let error as NSError {
print("Server failed to start with error: \(error)")
}
}
public func stop() {
http.stop()
}
internal func request(request:Request, reply: Reply) -> HttpResponse {
switch reply {
case .Text(let body):
return .OK(.Text(body))
case .JSON(let response):
return .OK(.Json(response.json()))
case .BadRequest:
return .BadRequest(.Text("Bad request."))
}
}
internal func dictionaryFromRequest(body: [UInt8]) -> [String: AnyObject]? {
let string = NSString(data: NSData(bytes: body, length: body.count), encoding: NSUTF8StringEncoding)
if let body = string?.componentsSeparatedByString("&") {
var dict: [String: AnyObject] = [:]
for argument in body {
let kv = argument.componentsSeparatedByString("=")
if let key = kv.first, value = kv.last {
dict[key] = value
}
}
return dict
}
return nil
}
internal func jsonFromRequest(string: String) -> [String: AnyObject]? {
guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else {
return nil
}
return (try? NSJSONSerialization.JSONObjectWithData(data, options: [])) as? [String: AnyObject] ?? nil
}
}
+120
View File
@@ -0,0 +1,120 @@
//
// SlackError.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public enum SlackError: String, ErrorType {
case AccountInactive = "account_inactive"
case AlreadyArchived = "already_archived"
case AlreadyInChannel = "already_in_channel"
case AlreadyPinned = "already_pinned"
case AlreadyReacted = "already_reacted"
case AlreadyStarred = "already_starred"
case BadClientSecret = "bad_client_secret"
case BadRedirectURI = "bad_redirect_uri"
case BadTimeStamp = "bad_timestamp"
case CantArchiveGeneral = "cant_archive_general"
case CantDelete = "cant_delete"
case CantDeleteFile = "cant_delete_file"
case CantDeleteMessage = "cant_delete_message"
case CantInvite = "cant_invite"
case CantInviteSelf = "cant_invite_self"
case CantKickFromGeneral = "cant_kick_from_general"
case CantKickFromLastChannel = "cant_kick_from_last_channel"
case CantKickSelf = "cant_kick_self"
case CantLeaveGeneral = "cant_leave_general"
case CantLeaveLastChannel = "cant_leave_last_channel"
case CantUpdateMessage = "cant_update_message"
case ChannelNotFound = "channel_not_found"
case ComplianceExportsPreventDeletion = "compliance_exports_prevent_deletion"
case EditWindowClosed = "edit_window_closed"
case FileCommentNotFound = "file_comment_not_found"
case FileDeleted = "file_deleted"
case FileNotFound = "file_not_found"
case FileNotShared = "file_not_shared"
case GroupContainsOthers = "group_contains_others"
case InvalidArgName = "invalid_arg_name"
case InvalidArrayArg = "invalid_array_arg"
case InvalidAuth = "invalid_auth"
case InvalidChannel = "invalid_channel"
case InvalidCharSet = "invalid_charset"
case InvalidClientID = "invalid_client_id"
case InvalidCode = "invalid_code"
case InvalidFormData = "invalid_form_data"
case InvalidName = "invalid_name"
case InvalidPostType = "invalid_post_type"
case InvalidPresence = "invalid_presence"
case InvalidTS = "invalid_timestamp"
case InvalidTSLatest = "invalid_ts_latest"
case InvalidTSOldest = "invalid_ts_oldest"
case IsArchived = "is_archived"
case LastMember = "last_member"
case LastRAChannel = "last_ra_channel"
case MessageNotFound = "message_not_found"
case MessageTooLong = "msg_too_long"
case MigrationInProgress = "migration_in_progress"
case MissingDuration = "missing_duration"
case MissingPostType = "missing_post_type"
case NameTaken = "name_taken"
case NoChannel = "no_channel"
case NoComment = "no_comment"
case NoItemSpecified = "no_item_specified"
case NoReaction = "no_reaction"
case NoText = "no_text"
case NotArchived = "not_archived"
case NotAuthed = "not_authed"
case NotEnoughUsers = "not_enough_users"
case NotInChannel = "not_in_channel"
case NotInGroup = "not_in_group"
case NotPinned = "not_pinned"
case NotStarred = "not_starred"
case OverPaginationLimit = "over_pagination_limit"
case PaidOnly = "paid_only"
case PermissionDenied = "perimssion_denied"
case PostingToGeneralChannelDenied = "posting_to_general_channel_denied"
case RateLimited = "rate_limited"
case RequestTimeout = "request_timeout"
case RestrictedAction = "restricted_action"
case SnoozeEndFailed = "snooze_end_failed"
case SnoozeFailed = "snooze_failed"
case SnoozeNotActive = "snooze_not_active"
case TooLong = "too_long"
case TooManyEmoji = "too_many_emoji"
case TooManyReactions = "too_many_reactions"
case TooManyUsers = "too_many_users"
case UnknownError
case UnknownType = "unknown_type"
case UserDisabled = "user_disabled"
case UserDoesNotOwnChannel = "user_does_not_own_channel"
case UserIsBot = "user_is_bot"
case UserIsRestricted = "user_is_restricted"
case UserIsUltraRestricted = "user_is_ultra_restricted"
case UserListNotSupplied = "user_list_not_supplied"
case UserNotFound = "user_not_found"
case UserNotVisible = "user_not_visible"
// Client
case ClientNetworkError
case ClientJSONError
case ClientOAuthError
// HTTP
case TooManyRequests
case UnknownHTTPError
}
+67
View File
@@ -0,0 +1,67 @@
//
// SlackKit.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public final class SlackKit: OAuthDelegate {
internal(set) public var oauth: OAuthServer?
internal(set) public var clients: [String: Client] = [:]
private let clientOptions: ClientOptions
// Initalization block
public var onClientInitalization: ((Client) -> Void)?
// If you already have an API token
public init(withAPIToken token: String, clientOptions: ClientOptions = ClientOptions()) {
self.clientOptions = clientOptions
let client = Client(apiToken: token)
dispatch_async(dispatch_get_main_queue(), {
self.onClientInitalization?(client)
})
clients[token] = client
client.connect(options: self.clientOptions)
}
// If you're going to be receiving and/or initiating OAuth requests, provide a client ID and secret
public init(clientID: String, clientSecret: String, state: String? = nil, redirectURI: String? = nil, port:in_port_t = 8080, forceIPV4: Bool = false, clientOptions: ClientOptions = ClientOptions()) {
self.clientOptions = clientOptions
oauth = try? OAuthServer(clientID: clientID, clientSecret: clientSecret, state: state, redirectURI: redirectURI, port: port, forceIPV4: forceIPV4, delegate: self)
}
internal func userAuthed(response: OAuthResponse) {
// User auth
if let token = response.accessToken {
let client = Client(apiToken: token)
self.onClientInitalization?(client)
clients[token] = client
}
// Bot User
if let token = response.bot?.botToken {
let client = Client(apiToken: token)
self.onClientInitalization?(client)
clients[token] = client
client.connect(options: self.clientOptions)
}
}
}
@@ -1,296 +0,0 @@
//
// SlackWebAPIErrorDispatcher.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 enum SlackError: ErrorType {
case AccountInactive
case AlreadyArchived
case AlreadyInChannel
case AlreadyPinned
case AlreadyReacted
case AlreadyStarred
case BadClientSecret
case BadRedirectURI
case BadTimeStamp
case CantArchiveGeneral
case CantDelete
case CantDeleteFile
case CantDeleteMessage
case CantInvite
case CantInviteSelf
case CantKickFromGeneral
case CantKickFromLastChannel
case CantKickSelf
case CantLeaveGeneral
case CantLeaveLastChannel
case CantUpdateMessage
case ChannelNotFound
case ComplianceExportsPreventDeletion
case EditWindowClosed
case FileCommentNotFound
case FileDeleted
case FileNotFound
case FileNotShared
case GroupContainsOthers
case InvalidArrayArg
case InvalidAuth
case InvalidChannel
case InvalidCharSet
case InvalidClientID
case InvalidCode
case InvalidFormData
case InvalidName
case InvalidPostType
case InvalidPresence
case InvalidTS
case InvalidTSLatest
case InvalidTSOldest
case IsArchived
case LastMember
case LastRAChannel
case MessageNotFound
case MessageTooLong
case MigrationInProgress
case MissingDuration
case MissingPostType
case NameTaken
case NoChannel
case NoComment
case NoItemSpecified
case NoReaction
case NoText
case NotArchived
case NotAuthed
case NotEnoughUsers
case NotInChannel
case NotInGroup
case NotPinned
case NotStarred
case OverPaginationLimit
case PaidOnly
case PermissionDenied
case PostingToGeneralChannelDenied
case RateLimited
case RequestTimeout
case RestrictedAction
case SnoozeEndFailed
case SnoozeFailed
case SnoozeNotActive
case TooLong
case TooManyEmoji
case TooManyReactions
case TooManyUsers
case UnknownError
case UnknownType
case UserDisabled
case UserDoesNotOwnChannel
case UserIsBot
case UserIsRestricted
case UserIsUltraRestricted
case UserListNotSupplied
case UserNotFound
case UserNotVisible
// Client
case ClientNetworkError
case ClientJSONError
// HTTP
case TooManyRequests
case UnknownHTTPError
}
internal struct ErrorDispatcher {
static func dispatch(error: String) -> SlackError {
switch error {
case "account_inactive":
return .AccountInactive
case "already_in_channel":
return .AlreadyInChannel
case "already_pinned":
return .AlreadyPinned
case "already_reacted":
return .AlreadyReacted
case "already_starred":
return .AlreadyStarred
case "bad_client_secret":
return .BadClientSecret
case "bad_redirect_uri":
return .BadRedirectURI
case "bad_timestamp":
return .BadTimeStamp
case "cant_delete":
return .CantDelete
case "cant_delete_file":
return .CantDeleteFile
case "cant_delete_message":
return .CantDeleteMessage
case "cant_invite":
return .CantInvite
case "cant_invite_self":
return .CantInviteSelf
case "cant_kick_from_general":
return .CantKickFromGeneral
case "cant_kick_from_last_channel":
return .CantKickFromLastChannel
case "cant_kick_self":
return .CantKickSelf
case "cant_leave_general":
return .CantLeaveGeneral
case "cant_leave_last_channel":
return .CantLeaveLastChannel
case "cant_update_message":
return .CantUpdateMessage
case "compliance_exports_prevent_deletion":
return .ComplianceExportsPreventDeletion
case "channel_not_found":
return .ChannelNotFound
case "edit_window_closed":
return .EditWindowClosed
case "file_comment_not_found":
return .FileCommentNotFound
case "file_deleted":
return .FileDeleted
case "file_not_found":
return .FileNotFound
case "file_not_shared":
return .FileNotShared
case "group_contains_others":
return .GroupContainsOthers
case "invalid_array_arg":
return .InvalidArrayArg
case "invalid_auth":
return .InvalidAuth
case "invalid_channel":
return .InvalidChannel
case "invalid_charset":
return .InvalidCharSet
case "invalid_client_id":
return .InvalidClientID
case "invalid_code":
return .InvalidCode
case "invalid_form_data":
return .InvalidFormData
case "invalid_name":
return .InvalidName
case "invalid_post_type":
return .InvalidPostType
case "invalid_presence":
return .InvalidPresence
case "invalid_timestamp":
return .InvalidTS
case "invalid_ts_latest":
return .InvalidTSLatest
case "invalid_ts_oldest":
return .InvalidTSOldest
case "is_archived":
return .IsArchived
case "last_member":
return .LastMember
case "last_ra_channel":
return .LastRAChannel
case "message_not_found":
return .MessageNotFound
case "msg_too_long":
return .MessageTooLong
case "migration_in_progress":
return .MigrationInProgress
case "missing_duration":
return .MissingDuration
case "missing_post_type":
return .MissingPostType
case "name_taken":
return .NameTaken
case "no_channel":
return .NoChannel
case "no_comment":
return .NoComment
case "no_reaction":
return .NoReaction
case "no_item_specified":
return .NoItemSpecified
case "no_text":
return .NoText
case "not_archived":
return .NotArchived
case "not_authed":
return .NotAuthed
case "not_enough_users":
return .NotEnoughUsers
case "not_in_channel":
return .NotInChannel
case "not_in_group":
return .NotInGroup
case "not_pinned":
return .NotPinned
case "not_starred":
return .NotStarred
case "over_pagination_limit":
return .OverPaginationLimit
case "paid_only":
return .PaidOnly
case "perimssion_denied":
return .PermissionDenied
case "posting_to_general_channel_denied":
return .PostingToGeneralChannelDenied
case "rate_limited":
return .RateLimited
case "request_timeout":
return .RequestTimeout
case "snooze_end_failed":
return .SnoozeEndFailed
case "snooze_failed":
return .SnoozeFailed
case "snooze_not_active":
return .SnoozeNotActive
case "too_long":
return .TooLong
case "too_many_emoji":
return .TooManyEmoji
case "too_many_reactions":
return .TooManyReactions
case "too_many_users":
return .TooManyUsers
case "unknown_type":
return .UnknownType
case "user_disabled":
return .UserDisabled
case "user_does_not_own_channel":
return .UserDoesNotOwnChannel
case "user_is_bot":
return .UserIsBot
case "user_is_restricted":
return .UserIsRestricted
case "user_is_ultra_restricted":
return .UserIsUltraRestricted
case "user_list_not_supplied":
return .UserListNotSupplied
case "user_not_found":
return .UserNotFound
case "user_not_visible":
return .UserNotVisible
default:
return .UnknownError
}
}
}
+2 -25
View File
@@ -23,7 +23,7 @@
public struct Team {
public let id: String
public let id: String?
internal(set) public var name: String?
internal(set) public var domain: String?
internal(set) public var emailDomain: String?
@@ -34,7 +34,7 @@ public struct Team {
internal(set) public var icon: TeamIcon?
internal init(team: [String: AnyObject]?) {
id = team?["id"] as! String
id = team?["id"] as? String
name = team?["name"] as? String
domain = team?["domain"] as? String
emailDomain = team?["email_domain"] as? String
@@ -45,26 +45,3 @@ public struct Team {
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
}
}
+44
View File
@@ -0,0 +1,44 @@
//
// TeamIcon.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct 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
}
}
+34
View File
@@ -0,0 +1,34 @@
//
// Topic.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct Topic {
public let value: String?
public let creator: String?
public let lastSet: Int?
internal init(topic: [String: AnyObject]?) {
value = topic?["value"] as? String
creator = topic?["creator"] as? String
lastSet = topic?["last_set"] as? Int
}
}
-264
View File
@@ -1,264 +0,0 @@
//
// Types.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
// MARK: - Edited
public struct Edited {
public let user: String?
public let ts: String?
internal init(edited:[String: AnyObject]?) {
user = edited?["user"] as? String
ts = edited?["ts"] as? String
}
}
// 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 {
messages.append(Message(message: message))
}
}
hasMore = history?["has_more"] as? Bool
}
}
// MARK: - Reaction
public struct Reaction {
public let name: String?
internal(set) public var user: String?
internal init(reaction:[String: AnyObject]?) {
name = reaction?["name"] as? String
}
internal init(name: String, user: String) {
self.name = name
self.user = user
}
static func reactionsFromArray(array: [[String: AnyObject]]?) -> [Reaction] {
var reactions = [Reaction]()
if let array = array {
for reaction in array {
if let users = reaction["users"] as? [String], name = reaction["name"] as? String {
for user in users {
reactions.append(Reaction(name: name, user: user))
}
}
}
}
return reactions
}
}
extension Reaction: Equatable {}
public func ==(lhs: Reaction, rhs: Reaction) -> Bool {
return lhs.name == rhs.name
}
// MARK: - Comment
public struct Comment {
public let id: String?
public let user: String?
internal(set) public var created: Int?
internal(set) public var comment: String?
internal(set) public var starred: Bool?
internal(set) public var stars: Int?
internal(set) public var reactions = [Reaction]()
internal init(comment:[String: AnyObject]?) {
id = comment?["id"] as? String
created = comment?["created"] as? Int
user = comment?["user"] as? String
starred = comment?["is_starred"] as? Bool
stars = comment?["num_stars"] as? Int
self.comment = comment?["comment"] as? String
}
internal init(id: String?) {
self.id = id
self.user = nil
}
}
extension Comment: Equatable {}
public func ==(lhs: Comment, rhs: Comment) -> Bool {
return lhs.id == rhs.id
}
// MARK: - Item
public struct Item {
public let type: String?
public let ts: String?
public let channel: String?
public let message: Message?
public let file: File?
public let comment: Comment?
public let fileCommentID: String?
internal init(item:[String: AnyObject]?) {
type = item?["type"] as? String
ts = item?["ts"] as? String
channel = item?["channel"] as? String
message = Message(message: item?["message"] as? [String: AnyObject])
// Comment and File can come across as Strings or Dictionaries
if let commentDictionary = item?["comment"] as? [String: AnyObject] {
comment = Comment(comment: commentDictionary)
} else {
comment = Comment(id: item?["comment"] as? String)
}
if let fileDictionary = item?["file"] as? [String: AnyObject] {
file = File(file: fileDictionary)
} else {
file = File(id: item?["file"] as? String)
}
fileCommentID = item?["file_comment"] as? String
}
}
extension Item: Equatable {}
public func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.type == rhs.type && lhs.channel == rhs.channel && lhs.file == rhs.file && lhs.comment == rhs.comment && lhs.message == rhs.message
}
// MARK: - Topic
public struct Topic {
public let value: String?
public let creator: String?
public let lastSet: Int?
internal init(topic: [String: AnyObject]?) {
value = topic?["value"] as? String
creator = topic?["creator"] as? String
lastSet = topic?["last_set"] as? Int
}
}
// MARK: - Do Not Disturb Status
public struct DoNotDisturbStatus {
internal(set) public var enabled: Bool?
internal(set) public var nextDoNotDisturbStart: Int?
internal(set) public var nextDoNotDisturbEnd: Int?
internal(set) public var snoozeEnabled: Bool?
internal(set) public var snoozeEndtime: Int?
internal init(status: [String: AnyObject]?) {
enabled = status?["dnd_enabled"] as? Bool
nextDoNotDisturbStart = status?["next_dnd_start_ts"] as? Int
nextDoNotDisturbEnd = status?["next_dnd_end_ts"] as? Int
snoozeEnabled = status?["snooze_enabled"] as? Bool
snoozeEndtime = status?["snooze_endtime"] as? Int
}
}
// 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 {
var cpf: CustomProfileField?
if let fieldDictionary = field as? [String: AnyObject] {
cpf = CustomProfileField(field: fieldDictionary)
} else {
cpf = CustomProfileField(id: field as? String)
}
if let id = cpf?.id { fields[id] = cpf }
}
}
}
internal init(customFields: [String: AnyObject]?) {
if let customFields = customFields {
for key in customFields.keys {
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
}
}
-2
View File
@@ -21,11 +21,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct UserGroup {
public let id: String?
internal(set) public var teamID: String?
public let isUserGroup: Bool?
internal(set) public var name: String?
@@ -1,5 +1,5 @@
//
// SlackWebAPI.swift
// WebAPI.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -23,8 +23,9 @@
import Foundation
internal enum SlackAPIEndpoint: String {
internal enum Endpoint: String {
case APITest = "api.test"
case AuthRevoke = "auth.revoke"
case AuthTest = "auth.test"
case ChannelsHistory = "channels.history"
case ChannelsInfo = "channels.info"
@@ -62,6 +63,7 @@ internal enum SlackAPIEndpoint: String {
case MPIMList = "mpim.list"
case MPIMMark = "mpim.mark"
case MPIMOpen = "mpim.open"
case OAuthAccess = "oauth.access"
case PinsAdd = "pins.add"
case PinsRemove = "pins.remove"
case ReactionsAdd = "reactions.add"
@@ -79,7 +81,7 @@ internal enum SlackAPIEndpoint: String {
case UsersSetPresence = "users.setPresence"
}
public class SlackWebAPI {
public final class WebAPI {
public typealias FailureClosure = (error: SlackError)->Void
@@ -107,19 +109,15 @@ public class SlackWebAPI {
private let networkInterface: NetworkInterface
private let token: String
init(networkInterface: NetworkInterface, token: String) {
self.networkInterface = networkInterface
public init(token: String) {
self.networkInterface = NetworkInterface()
self.token = token
}
convenience public init(client: Client) {
self.init(networkInterface: client.api, token: client.token)
}
//MARK: - RTM
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]
networkInterface.request(.RTMStart, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.RTMStart, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response: response)
}) {(error) -> Void in
@@ -127,7 +125,7 @@ public class SlackWebAPI {
}
}
//MARK: - Auth Test
//MARK: - Auth
public func authenticationTest(success: ((authenticated: Bool)->Void)?, failure: FailureClosure?) {
networkInterface.request(.AuthTest, token: token, parameters: nil, successClosure: {
(response) -> Void in
@@ -137,6 +135,26 @@ public class SlackWebAPI {
}
}
public static func oauthAccess(clientID: String, clientSecret: String, code: String, redirectURI: String? = nil, success: ((response: [String: AnyObject])->Void)?, failure: ((SlackError)->Void)?) {
let parameters: [String: AnyObject?] = ["client_id": clientID, "client_secret": clientSecret, "code": code, "redirect_uri": redirectURI]
NetworkInterface().request(.OAuthAccess, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response:response)
}) {(error) -> Void in
failure?(error)
}
}
public static func oauthRevoke(token: String, test: Bool? = nil, success: ((revoked:Bool)->Void)?, failure: ((SlackError)->Void)?) {
let parameters: [String: AnyObject?] = ["token": token, "test": test]
NetworkInterface().request(.AuthRevoke, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(revoked:true)
}) {(error) -> Void in
failure?(error)
}
}
//MARK: - Channels
public func channelHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
history(.ChannelsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
@@ -204,7 +222,7 @@ public class SlackWebAPI {
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]
networkInterface.request(.ChatPostMessage, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.ChatPostMessage, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) -> Void in
@@ -214,7 +232,7 @@ public class SlackWebAPI {
public func updateMessage(channel: String, ts: String, message: String, attachments: [Attachment?]? = nil, parse:ParseMode = .None, linkNames: Bool = false, success: ((updated: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping(), "parse": parse.rawValue, "link_names": linkNames, "attachments":encodeAttachments(attachments)]
networkInterface.request(.ChatUpdate, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.ChatUpdate, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(updated: true)
}) {(error) -> Void in
@@ -225,7 +243,7 @@ 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]
networkInterface.request(.DNDInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.DNDInfo, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(status: DoNotDisturbStatus(status: response))
}) {(error) -> Void in
@@ -235,7 +253,7 @@ public class SlackWebAPI {
public func dndTeamInfo(users: [String]? = nil, success: ((statuses: [String: DoNotDisturbStatus])->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["users":users?.joinWithSeparator(",")]
networkInterface.request(.DNDTeamInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.DNDTeamInfo, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
guard let usersDictionary = response["users"] as? [String: AnyObject] else {
success?(statuses: [:])
@@ -287,7 +305,7 @@ public class SlackWebAPI {
public func uploadFile(file: NSData, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "filename": filename, "filetype":filetype, "title":title, "initial_comment":initialComment, "channels":channels?.joinWithSeparator(",")]
networkInterface.uploadRequest(token, data: file, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.uploadRequest(token, data: file, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(file: File(file: response["file"] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -515,9 +533,9 @@ public class SlackWebAPI {
}
}
private func pin(endpoint: SlackAPIEndpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
private func pin(endpoint: Endpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel":channel, "file":file, "file_comment":fileComment, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}){(error) -> Void in
@@ -546,9 +564,9 @@ public class SlackWebAPI {
}
}
private func react(endpoint: SlackAPIEndpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
private func react(endpoint: Endpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["name":name, "file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -577,9 +595,9 @@ public class SlackWebAPI {
}
}
private func star(endpoint: SlackAPIEndpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
private func star(endpoint: Endpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: WebAPI.filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -649,7 +667,7 @@ public class SlackWebAPI {
}
//MARK: - Channel Utilities
private func close(endpoint: SlackAPIEndpoint, channelID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
private func close(endpoint: Endpoint, channelID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel":channelID]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -659,7 +677,7 @@ public class SlackWebAPI {
}
}
private func history(endpoint: SlackAPIEndpoint, id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
private func history(endpoint: Endpoint, 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]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -669,7 +687,7 @@ public class SlackWebAPI {
}
}
private func info(endpoint: SlackAPIEndpoint, type: ChannelType, id: String, success: ((channel: Channel)->Void)?, failure: FailureClosure?) {
private func info(endpoint: Endpoint, type: ChannelType, id: String, success: ((channel: Channel)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": id]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -679,7 +697,7 @@ public class SlackWebAPI {
}
}
private func list(endpoint: SlackAPIEndpoint, type: ChannelType, excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
private func list(endpoint: Endpoint, type: ChannelType, excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["exclude_archived": excludeArchived]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -689,7 +707,7 @@ public class SlackWebAPI {
}
}
private func mark(endpoint: SlackAPIEndpoint, channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
private func mark(endpoint: Endpoint, channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, "ts": timestamp]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -699,7 +717,7 @@ public class SlackWebAPI {
}
}
private func setInfo(endpoint: SlackAPIEndpoint, type: InfoType, channel: String, text: String, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
private func setInfo(endpoint: Endpoint, type: InfoType, channel: String, text: String, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, type.rawValue: text]
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
@@ -708,17 +726,6 @@ public class SlackWebAPI {
failure?(error: error)
}
}
//MARK: - Filter Nil Parameters
private func filterNilParameters(parameters: [String: AnyObject?]) -> [String: AnyObject] {
var finalParameters = [String: AnyObject]()
for key in parameters.keys {
if parameters[key] != nil {
finalParameters[key] = parameters[key]!
}
}
return finalParameters
}
//MARK: - Encode Attachments
private func encodeAttachments(attachments: [Attachment?]?) -> NSString? {
@@ -740,6 +747,17 @@ public class SlackWebAPI {
return nil
}
//MARK: - Filter Nil Parameters
internal static func filterNilParameters(parameters: [String: AnyObject?]) -> [String: AnyObject] {
var finalParameters = [String: AnyObject]()
for (key, value) in parameters {
if let unwrapped = value {
finalParameters[key] = unwrapped
}
}
return finalParameters
}
//MARK: - Enumerate Do Not Disturb Status
private func enumerateDNDStatuses(statuses: [String: AnyObject]) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
+50
View File
@@ -0,0 +1,50 @@
//
// WebhookRequest.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal struct WebhookRequest: Request {
let token: String?
let teamID: String
let teamDomain: String
let channelID: String
let channelName: String
let userID: String
let userName: String
let command: String
let text: String
let responseURL: String
init(request: [String: AnyObject]?) {
token = request?["token"] as? String
teamID = request?["team_id"] as? String ?? ""
teamDomain = request?["team_domain"] as? String ?? ""
channelID = request?["channel_id"] as? String ?? ""
channelName = request?["channel_name"] as? String ?? ""
userID = request?["user_id"] as? String ?? ""
userName = request?["user_name"] as? String ?? ""
command = request?["command"] as? String ?? ""
text = request?["text"] as? String ?? ""
responseURL = request?["response_url"] as? String ?? ""
}
}
+50
View File
@@ -0,0 +1,50 @@
//
// WebhookServer.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public class WebhookServer: Server {
public init(token: String, route: String, response: Response) {
super.init(token: token)
addRoute(route, response: response)
}
public func addRoute(route: String, response: Response) {
http["/\(route)"] = { request in
let webhookRequest = WebhookRequest(request: self.dictionaryFromRequest(request.body))
if webhookRequest.token == self.token {
return self.request(webhookRequest, reply: self.replyForResponse(response))
} else {
return .BadRequest(.Text("Bad request."))
}
}
}
private func replyForResponse(response: Response) -> Reply {
if response.attachments == nil && response.responseType == nil {
return Reply.Text(body: response.text)
} else {
return Reply.JSON(response: response)
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.1</string>
<string>2.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.1</string>
<string>2.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.1</string>
<string>2.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>