Skip to content
This repository was archived by the owner on Dec 2, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PODS:
- Starscream (1.0.2)
- Starscream (1.1.2)

DEPENDENCIES:
- Starscream

SPEC CHECKSUMS:
Starscream: 40e2c4c1c770d811f24116b8b7dbeb4542c56767
Starscream: 58a12fd35a3cb6aaa105716c2d42765f7c1c732f

COCOAPODS: 0.39.0
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ If you want to receive messages from the Slack RTM API, connect to it.
client.connect()
```

You can also set options for a ping/pong interval, timeout interval, and automatic reconnection:
```swift
client.connect(pingInterval: 2, timeout: 10, reconnect: false)
```

Once connected, the client will begin to consume any messages sent by the Slack RTM API.

####Web API Methods
Expand All @@ -65,6 +70,9 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- chat.postMessage
- chat.update
- emoji.list
- files.comments.add
- files.comments.edit
- files.comments.delete
- files.delete
- files.upload
- groups.close
Expand Down Expand Up @@ -187,8 +195,8 @@ func itemStarred(item: Item, star: Bool)

#####ReactionEventsDelegate
```swift
func reactionAdded(reaction: String?, item: Item?)
func reactionRemoved(reaction: String?, item: Item?)
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
```

#####TeamEventsDelegate
Expand Down
3 changes: 2 additions & 1 deletion SlackKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.0.2'
s.dependency 'Starscream', '~> 1.1.2'
end

8 changes: 2 additions & 6 deletions SlackKit/Sources/Channel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public struct Channel {
internal(set) public var unread: Int?
internal(set) public var unreadCountDisplay: Int?
internal(set) public var hasPins: Bool?
internal(set) public var members = [String]()
internal(set) public var members: [String]?
// Client use
internal(set) public var pinnedItems = [Item]()
internal(set) public var usersTyping = [String]()
Expand All @@ -70,11 +70,7 @@ public struct Channel {
unread = channel?["unread_count"] as? Int
unreadCountDisplay = channel?["unread_count_display"] as? Int
hasPins = channel?["has_pins"] as? Bool

if let members = channel?["members"] as? [String] {
self.members = members
}

members = channel?["members"] as? [String]
}

internal init?(id:String?) {
Expand Down
90 changes: 80 additions & 10 deletions SlackKit/Sources/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class Client: WebSocketDelegate {
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?

internal var token = "SLACK_AUTH_TOKEN"

public func setAuthToken(token: String) {
self.token = token
}
Expand All @@ -63,15 +63,25 @@ public class Client: WebSocketDelegate {
}

internal var webSocket: WebSocket?
internal let api = NetworkInterface()
private var dispatcher: EventDispatcher?

internal let api = NetworkInterface()
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
internal var ping: Double?
internal var pong: Double?

internal var pingInterval: NSTimeInterval?
internal var timeout: NSTimeInterval?
internal var reconnect: Bool?

required public init(apiToken: String) {
self.token = apiToken
}

public func connect() {
public func connect(pingInterval pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
dispatcher = EventDispatcher(client: self)
webAPI.rtmStart(success: {
(response) -> Void in
Expand All @@ -85,19 +95,24 @@ public class Client: WebSocketDelegate {
}, failure:nil)
}

public func disconnect() {
webSocket?.disconnect()
}

//MARK: - Message send
public func sendMessage(message: String, channelID: String) {
if (connected) {
if let data = formatMessageToSlackJsonString(msg: message, channel: channelID) {
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
webSocket?.writeString(string as! String)
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
}
}
}
}

private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
let json: [String: AnyObject] = [
"id": NSDate().timeIntervalSince1970,
"id": NSDate().slackTimestamp(),
"type": "message",
"channel": message.channel,
"text": message.msg.slackFormatEscaping()
Expand All @@ -121,6 +136,52 @@ public class Client: WebSocketDelegate {
sentMessages[ts!.stringValue] = Message(message: message)
}

//MARK: - RTM Ping
private func pingRTMServerAtInterval(interval: NSTimeInterval) {
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
dispatch_after(delay, pingPongQueue, {
if self.connected && self.timeoutCheck() {
self.sendRTMPing()
self.pingRTMServerAtInterval(interval)
} else {
self.disconnect()
}
})
}

private func sendRTMPing() {
if connected {
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
"type": "ping",
]
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
if let writePing = string as? String {
ping = json["id"] as? Double
webSocket?.writeString(writePing)
}
}
catch _ {

}
}
}

private func timeoutCheck() -> Bool {
if let pong = pong, ping = ping, timeout = timeout {
if pong - ping < timeout {
return true
} else {
return false
}
// Ping-pong or timeout not configured
} else {
return true
}
}

//MARK: - Client setup
internal func initialSetup(json: [String: AnyObject]) {
team = Team(team: json["team"] as? [String: AnyObject])
Expand Down Expand Up @@ -207,14 +268,21 @@ public class Client: WebSocketDelegate {
}

// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {}
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = pingInterval {
pingRTMServerAtInterval(pingInterval)
}
}

public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
connected = false
authenticated = false
webSocket = nil
if let delegate = slackEventsDelegate {
delegate.clientDisconnected()
dispatcher = nil
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
}
}

Expand All @@ -223,7 +291,9 @@ public class Client: WebSocketDelegate {
return
}
do {
try dispatcher?.dispatch(NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject])
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
dispatcher?.dispatch(json)
}
}
catch _ {

Expand Down
24 changes: 8 additions & 16 deletions SlackKit/Sources/ClientExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,11 @@ import Foundation
extension Client {

//MARK: - User & Channel
public func getChannelOrUserIdByName(name: String) -> String? {
if (name[name.startIndex] == "@") {
return getUserIdByName(name)
} else if (name[name.startIndex] == "C") {
return getChannelIDByName(name)
}
return nil
}

public func getChannelIDByName(name: String) -> String? {
return channels.filter{$0.1.name == stripString(name)}.first?.0
}

public func getUserIdByName(name: String) -> String? {
public func getUserIDByName(name: String) -> String? {
return users.filter{$0.1.name == stripString(name)}.first?.0
}

Expand All @@ -54,13 +45,14 @@ extension Client {
}

//MARK: - Utilities
internal func stripString(var string: String) -> String {
internal func stripString(string: String) -> String? {
var strippedString: String?
if string[string.startIndex] == "@" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
} else if string[string.startIndex] == "#" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
}
return string
return strippedString
}

}
Expand All @@ -78,8 +70,8 @@ internal extension String {

public extension NSDate {

func slackTimestamp() -> String {
return NSNumber(double: timeIntervalSince1970).stringValue
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}

}
1 change: 1 addition & 0 deletions SlackKit/Sources/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ internal enum EventType: String {
case FileCommentDeleted = "file_comment_deleted"
case PinAdded = "pin_added"
case PinRemoved = "pin_removed"
case Pong = "pong"
case PresenceChange = "presence_change"
case ManualPresenceChange = "manual_presence_change"
case PrefChange = "pref_change"
Expand Down
4 changes: 3 additions & 1 deletion SlackKit/Sources/EventDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ internal class EventDispatcher {
handler.pinAdded(event)
case .PinRemoved:
handler.pinRemoved(event)
case .Pong:
handler.pong(event)
case .PresenceChange:
handler.presenceChange(event)
case .ManualPresenceChange:
Expand Down Expand Up @@ -146,7 +148,7 @@ internal class EventDispatcher {
// Other clients should ignore this event.
break
case .TeamMigrationStarted:
client.connect()
client.connect(pingInterval: client.pingInterval, timeout: client.timeout, reconnect: client.reconnect)
case .ReconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
Expand Down
9 changes: 7 additions & 2 deletions SlackKit/Sources/EventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ internal class EventHandler {
}
}

//MARK: - Pong
func pong(event: Event) {
client.pong = event.replyTo
}

//MARK: - Messages
func messageSent(event: Event) {
if let reply = event.replyTo, message = client.sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts {
Expand Down Expand Up @@ -147,8 +152,8 @@ internal class EventHandler {

func channelLeft(event: Event) {
if let channel = event.channel, id = channel.id, userID = client.authenticatedUser?.id {
if let index = client.channels[id]?.members.indexOf(userID) {
client.channels[id]?.members.removeAtIndex(index)
if let index = client.channels[id]?.members?.indexOf(userID) {
client.channels[id]?.members?.removeAtIndex(index)

if let delegate = client.channelEventsDelegate {
delegate.channelLeft(channel)
Expand Down
4 changes: 2 additions & 2 deletions SlackKit/Sources/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public struct File {
internal(set) public var comments = [String: Comment]()
internal(set) public var reactions = [String: Reaction]()

init?(file:[String: AnyObject]?) {
public init?(file:[String: AnyObject]?) {
id = file?["id"] as? String
created = file?["created"] as? Int
name = file?["name"] as? String
Expand Down Expand Up @@ -105,7 +105,7 @@ public struct File {

}

init?(id:String?) {
internal init?(id:String?) {
self.id = id
created = nil
name = nil
Expand Down
6 changes: 6 additions & 0 deletions SlackKit/Sources/SlackWebAPIErrorDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public enum SlackError: ErrorType {
case BadRedirectURI
case BadTimeStamp
case CantArchiveGeneral
case CantDelete
case CantDeleteFile
case CantDeleteMessage
case CantInvite
Expand Down Expand Up @@ -75,6 +76,7 @@ public enum SlackError: ErrorType {
case MissingPostType
case NameTaken
case NoChannel
case NoComment
case NoItemSpecified
case NoReaction
case NoText
Expand Down Expand Up @@ -131,6 +133,8 @@ internal struct ErrorDispatcher {
return .BadRedirectURI
case "bad_timestamp":
return .BadTimeStamp
case "cant_delete":
return .CantDelete
case "cant_delete_file":
return .CantDeleteFile
case "cant_delete_message":
Expand Down Expand Up @@ -213,6 +217,8 @@ internal struct ErrorDispatcher {
return .NameTaken
case "no_channel":
return .NoChannel
case "no_comment":
return .NoComment
case "no_reaction":
return .NoReaction
case "no_item_specified":
Expand Down