Files
2021-12-28 09:49:53 -05:00

1713 lines
59 KiB
Swift
Executable File

//
// WebAPI.swift
//
// Copyright © 2017 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.
//swiftlint:disable file_length
import Foundation
#if !COCOAPODS
@_exported import SKCore
#endif
public final class WebAPI {
public typealias SuccessClosure = (_ success: Bool) -> Void
public typealias FailureClosure = (_ error: SlackError) -> Void
public typealias CommentClosure = (_ comment: Comment) -> Void
public typealias ChannelClosure = (_ channel: Channel) -> Void
public typealias ChannelsClosure = (_ channels: [Channel], _ nextCursor: String?) -> Void
public typealias MessageClosure = (_ message: Message) -> Void
public typealias HistoryClosure = (_ history: History) -> Void
public typealias FileClosure = (_ file: File) -> Void
public typealias ItemsClosure = (_ items: [Item]?) -> Void
public typealias AuthTestClosure = (_ user: String?, _ team: String?) -> Void
public enum InfoType: String {
case purpose, topic
}
public enum ParseMode: String {
case full, none
}
public enum Presence: String {
case auto, away
}
fileprivate enum ChannelType: String {
case channel, group, im
}
public enum ConversationType: String {
case public_channel, private_channel, mpim, im
}
fileprivate let networkInterface: NetworkInterface
fileprivate let token: String
public init(token: String) {
self.networkInterface = NetworkInterface()
self.token = token
}
}
// MARK: - RTM
extension WebAPI {
public static func rtmConnect(
token: String,
batchPresenceAware: Bool = false,
presenceSub: Bool = false,
success: ((_ response: [String: Any]) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] =
[
"batch_presence_aware": batchPresenceAware,
"presence_sub": presenceSub
]
NetworkInterface().request(.rtmConnect, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Auth
extension WebAPI {
public func authenticationTest(success: AuthTestClosure?, failure: FailureClosure?) {
networkInterface.request(.authTest, accessToken: token, parameters: [:], successClosure: { (response) in
success?(response["user_id"] as? String, response["team_id"] as? String)
}) {(error) in
failure?(error)
}
}
public static func oauthAccess(clientID: String, clientSecret: String, code: String, redirectURI: String? = nil) -> [String: Any]? {
let parameters: [String: Any?] = ["client_id": clientID, "client_secret": clientSecret, "code": code, "redirect_uri": redirectURI]
return NetworkInterface().synchronusRequest(.oauthAccess, parameters: parameters)
}
public static func oauthRevoke(
token: String,
test: Bool? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = ["test": test]
NetworkInterface().request(.authRevoke, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Channels
extension WebAPI {
public func channelHistory(
id: String,
latest: String = "\(Date().timeIntervalSince1970)",
oldest: String = "0", inclusive: Bool = false,
count: Int = 100, unreads: Bool = false,
success: HistoryClosure?,
failure: FailureClosure?
) {
history(.channelsHistory,
id: id,
latest: latest,
oldest: oldest,
inclusive: inclusive,
count: count,
unreads: unreads,
success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func channelsLeave(_ channel: String, success: SuccessClosure?, failure: FailureClosure?) {
leave(.channelsLeave, channel: channel, success: success, failure: failure)
}
public func channelsUnarchive(_ channel: String, success: SuccessClosure?, failure: FailureClosure?) {
unarchive(.channelsUnarchive, channel: channel, success: success, failure: failure)
}
public func channelsRename(_ channel: String, name: String, validate: Bool, success: ChannelClosure?, failure: FailureClosure?) {
rename(.channelsRename, channel: channel, name: name, validate: validate, success: success, failure: failure)
}
public func channelsKick(_ channel: String, user: String, success: SuccessClosure?, failure: FailureClosure?) {
kick(.channelsKick, channel: channel, user: user, success: success, failure: failure)
}
public func setChannelPurpose(channel: String, purpose: String, success: SuccessClosure?, failure: FailureClosure?) {
setInfo(.channelsSetPurpose, type: .purpose, channel: channel, text: purpose, success: {(purposeSet) in
success?(purposeSet)
}) {(error) in
failure?(error)
}
}
public func setChannelTopic(channel: String, topic: String, success: SuccessClosure?, failure: FailureClosure?) {
setInfo(.channelsSetTopic, type: .topic, channel: channel, text: topic, success: {(topicSet) in
success?(topicSet)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Messaging
extension WebAPI {
public func deleteMessage(channel: String, ts: String, success: SuccessClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channel, "ts": ts]
networkInterface.request(.chatDelete, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
public func sendMessage(
channel: String,
text: String,
username: String? = nil,
asUser: Bool? = nil,
parse: ParseMode? = nil,
linkNames: Bool? = nil,
attachments: [Attachment?]? = nil,
blocks: [Block]? = nil,
unfurlLinks: Bool? = nil,
unfurlMedia: Bool? = nil,
iconURL: String? = nil,
iconEmoji: String? = nil,
success: (((ts: String?, channel: String?)) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel,
"text": text,
"as_user": asUser,
"parse": parse?.rawValue,
"link_names": linkNames,
"unfurl_links": unfurlLinks,
"unfurl_media": unfurlMedia,
"username": username,
"icon_url": iconURL,
"icon_emoji": iconEmoji,
"attachments": encodeAttachments(attachments),
"blocks": encodeBlocks(blocks)
]
networkInterface.request(.chatPostMessage, accessToken: token, parameters: parameters, successClosure: {(response) in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func sendThreadedMessage(
channel: String,
thread: String,
text: String,
broadcastReply: Bool = false,
username: String? = nil,
asUser: Bool? = nil,
parse: ParseMode? = nil,
linkNames: Bool? = nil,
attachments: [Attachment?]? = nil,
unfurlLinks: Bool? = nil,
unfurlMedia: Bool? = nil,
iconURL: String? = nil,
iconEmoji: String? = nil,
success: (((ts: String?, channel: String?)) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel,
"thread_ts": thread,
"text": text,
"reply_broadcast": broadcastReply,
"as_user": asUser,
"parse": parse?.rawValue,
"link_names": linkNames,
"unfurl_links": unfurlLinks,
"unfurl_media": unfurlMedia,
"username": username,
"icon_url": iconURL,
"icon_emoji": iconEmoji,
"attachments": encodeAttachments(attachments)
]
networkInterface.request(.chatPostMessage, accessToken: token, parameters: parameters, successClosure: {(response) in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func sendEphemeral(
channel: String,
text: String,
user: String,
thread: String? = nil,
asUser: Bool? = nil,
attachments: [Attachment?]? = nil,
blocks: [Block]? = nil,
linkNames: Bool? = nil,
parse: ParseMode? = nil,
success: (((ts: String?, channel: String?)) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel,
"text": text,
"user": user,
"thread_ts": thread,
"as_user": asUser,
"attachments": encodeAttachments(attachments),
"blocks": encodeBlocks(blocks),
"link_names": linkNames,
"parse": parse?.rawValue,
]
networkInterface.request(.chatPostEphemeral, accessToken: token, parameters: parameters, successClosure: {(response) in
success?((ts: response["message_ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func sendMeMessage(
channel: String,
text: String,
success: (((ts: String?, channel: String?)) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = ["channel": channel, "text": text]
networkInterface.request(.chatMeMessage, accessToken: token, parameters: parameters, successClosure: {(response) in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) in
failure?(error)
}
}
public func updateMessage(
channel: String,
ts: String,
message: String,
attachments: [Attachment?]? = nil,
parse: ParseMode = .none,
linkNames: Bool = false,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel,
"ts": ts,
"text": message,
"parse": parse.rawValue,
"link_names": linkNames,
"attachments": encodeAttachments(attachments)
]
networkInterface.request(.chatUpdate, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Do Not Disturb
extension WebAPI {
public func dndInfo(user: String? = nil, success: ((_ status: DoNotDisturbStatus) -> Void)?, failure: FailureClosure?) {
let parameters: [String: Any?] = ["user": user]
networkInterface.request(.dndInfo, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(DoNotDisturbStatus(status: response))
}) {(error) in
failure?(error)
}
}
public func dndTeamInfo(
users: [String]? = nil,
success: ((_ statuses: [String: DoNotDisturbStatus]) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = ["users": users?.joined(separator: ",")]
networkInterface.request(.dndTeamInfo, accessToken: token, parameters: parameters, successClosure: {(response) in
guard let usersDictionary = response["users"] as? [String: Any] else {
success?([:])
return
}
success?(self.enumerateDNDStatuses(usersDictionary))
}) {(error) in
failure?(error)
}
}
}
// MARK: - Emoji
extension WebAPI {
public func emojiList(success: ((_ emojiList: [String: Any]?) -> Void)?, failure: FailureClosure?) {
networkInterface.request(.emojiList, accessToken: token, parameters: [:], successClosure: {(response) in
success?(response["emoji"] as? [String: Any])
}) {(error) in
failure?(error)
}
}
}
// MARK: - Files
extension WebAPI {
public func deleteFile(fileID: String, success: SuccessClosure?, failure: FailureClosure?) {
let parameters = ["file": fileID]
networkInterface.request(.filesDelete, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
public func fileInfo(
fileID: String,
count: Int = 100,
page: Int = 1,
success: FileClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["file": fileID, "count": count, "page": page]
networkInterface.request(.filesInfo, accessToken: token, parameters: parameters, successClosure: {(response) in
var file = File(file: response["file"] as? [String: Any])
(response["comments"] as? [[String: Any]])?.forEach { comment in
let comment = Comment(comment: comment)
if let id = comment.id {
file.comments[id] = comment
}
}
success?(file)
}) {(error) in
failure?(error)
}
}
public func uploadFile(
file: Data,
filename: String,
filetype: String = "auto",
title: String? = nil,
initialComment: String? = nil,
channels: [String]? = nil,
ts: String? = nil,
success: FileClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"filename": filename,
"filetype": filetype,
"title": title,
"initial_comment": initialComment,
"channels": channels?.joined(separator: ","),
"thread_ts": ts
]
networkInterface.uploadRequest(data: file, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(File(file: response["file"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
}
// MARK: - File Comments
extension WebAPI {
public func addFileComment(fileID: String, comment: String, success: CommentClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "comment": comment]
networkInterface.request(.filesCommentsAdd, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func editFileComment(fileID: String, commentID: String, comment: String, success: CommentClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "id": commentID, "comment": comment]
networkInterface.request(.filesCommentsEdit, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(Comment(comment: response["comment"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func deleteFileComment(fileID: String, commentID: String, success: SuccessClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["file": fileID, "id": commentID]
networkInterface.request(.filesCommentsDelete, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Groups
extension WebAPI {
public func closeGroup(groupID: String, success: SuccessClosure?, failure: FailureClosure?) {
close(.groupsClose, channelID: groupID, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func groupHistory(
id: String,
latest: String = "\(Date().timeIntervalSince1970)",
oldest: String = "0",
inclusive: Bool = false,
count: Int = 100,
unreads: Bool = false,
success: HistoryClosure?,
failure: FailureClosure?
) {
history(.groupsHistory,
id: id,
latest: latest,
oldest: oldest,
inclusive: inclusive,
count: count,
unreads: unreads,
success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func groupInfo(id: String, success: ChannelClosure?, failure: FailureClosure?) {
info(.groupsInfo, type:.group, id: id, success: {(channel) in
success?(channel)
}) {(error) in
failure?(error)
}
}
public func groupsList(
excludeArchived: Bool = false,
excludeMembers: Bool = false,
success: ((_ channels: [[String: Any]]?) -> Void)?,
failure: FailureClosure?
) {
list(.groupsList, type:.group, excludeArchived: excludeArchived, excludeMembers: excludeMembers, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markGroup(channel: String, timestamp: String, success: ((_ ts: String) -> Void)?, failure: FailureClosure?) {
mark(.groupsMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(ts)
}) {(error) in
failure?(error)
}
}
public func openGroup(channel: String, success: SuccessClosure?, failure: FailureClosure?) {
let parameters = ["channel": channel]
networkInterface.request(.groupsOpen, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
public func setGroupPurpose(channel: String, purpose: String, success: SuccessClosure?, failure: FailureClosure?) {
setInfo(.groupsSetPurpose, type: .purpose, channel: channel, text: purpose, success: {(purposeSet) in
success?(purposeSet)
}) {(error) in
failure?(error)
}
}
public func setGroupTopic(channel: String, topic: String, success: SuccessClosure?, failure: FailureClosure?) {
setInfo(.groupsSetTopic, type: .topic, channel: channel, text: topic, success: {(topicSet) in
success?(topicSet)
}) {(error) in
failure?(error)
}
}
}
// MARK: - IM
extension WebAPI {
public func closeIM(channel: String, success: SuccessClosure?, failure: FailureClosure?) {
close(.imClose, channelID: channel, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func imHistory(
id: String,
latest: String = "\(Date().timeIntervalSince1970)",
oldest: String = "0",
inclusive: Bool = false,
count: Int = 100,
unreads: Bool = false,
success: HistoryClosure?,
failure: FailureClosure?
) {
history(.imHistory,
id: id,
latest: latest,
oldest: oldest,
inclusive: inclusive,
count: count,
unreads: unreads,
success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func imsList(
excludeArchived: Bool = false,
excludeMembers: Bool = false,
success: ((_ channels: [[String: Any]]?) -> Void)?,
failure: FailureClosure?
) {
list(.imList, type:.im, excludeArchived: excludeArchived, excludeMembers: excludeMembers, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markIM(channel: String, timestamp: String, success: ((_ ts: String) -> Void)?, failure: FailureClosure?) {
mark(.imMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(ts)
}) {(error) in
failure?(error)
}
}
}
// MARK: - MPIM
extension WebAPI {
public func closeMPIM(channel: String, success: SuccessClosure?, failure: FailureClosure?) {
close(.mpimClose, channelID: channel, success: {(closed) in
success?(closed)
}) {(error) in
failure?(error)
}
}
public func mpimHistory(
id: String,
latest: String = "\(Date().timeIntervalSince1970)",
oldest: String = "0",
inclusive: Bool = false,
count: Int = 100,
unreads: Bool = false,
success: HistoryClosure?,
failure: FailureClosure?
) {
history(.mpimHistory,
id: id,
latest: latest,
oldest: oldest,
inclusive: inclusive,
count: count,
unreads: unreads,
success: {(history) in
success?(history)
}) {(error) in
failure?(error)
}
}
public func mpimsList(
excludeArchived: Bool = false,
excludeMembers: Bool = false,
success: ((_ channels: [[String: Any]]?) -> Void)?,
failure: FailureClosure?
) {
list(.mpimList, type:.group, excludeArchived: excludeArchived, excludeMembers: excludeMembers, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
public func markMPIM(channel: String, timestamp: String, success: ((_ ts: String) -> Void)?, failure: FailureClosure?) {
mark(.mpimMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(ts)
}) {(error) in
failure?(error)
}
}
public func openMPIM(userIDs: [String], success: ((_ mpimID: String?) -> Void)?, failure: FailureClosure?) {
let parameters = ["users": userIDs.joined(separator: ",")]
networkInterface.request(.mpimOpen, accessToken: token, parameters: parameters, successClosure: {(response) in
let group = response["group"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Pins
extension WebAPI {
public func pinsList(
channel: String,
success: ItemsClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel
]
networkInterface.request(.pinsList, accessToken: token, parameters: parameters, successClosure: { response in
let items = response["items"] as? [[String: Any]]
success?(items?.map({ Item(item: $0) }))
}) {(error) in
failure?(error)
}
}
public func pinItem(
channel: String,
file: String? = nil,
fileComment: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
pin(.pinsAdd, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
public func unpinItem(
channel: String,
file: String? = nil,
fileComment: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
pin(.pinsRemove, channel: channel, file: file, fileComment: fileComment, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func pin(
_ endpoint: Endpoint,
channel: String,
file: String? = nil,
fileComment: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"channel": channel,
"file": file,
"file_comment": fileComment,
"timestamp": timestamp
]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Reactions
extension WebAPI {
public func addReactionToMessage(name: String, channel: String, timestamp: String, success: SuccessClosure?, failure: FailureClosure?) {
addReaction(name: name, channel: channel, timestamp: timestamp, success: success, failure: failure)
}
public func addReactionToFile(name: String, file: String, success: SuccessClosure?, failure: FailureClosure?) {
addReaction(name: name, file: file, success: success, failure: failure)
}
public func addReactionToFileComment(name: String, fileComment: String, success: SuccessClosure?, failure: FailureClosure?) {
addReaction(name: name, fileComment: fileComment, success: success, failure: failure)
}
private func addReaction(
name: String,
file: String? = nil,
fileComment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
react(.reactionsAdd, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
public func removeReactionFromMessage(
name: String,
channel: String,
timestamp: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
removeReaction(name: name, channel: channel, timestamp: timestamp, success: success, failure: failure)
}
public func removeReactionFromFile(name: String, file: String, success: SuccessClosure?, failure: FailureClosure?) {
removeReaction(name: name, file: file, success: success, failure: failure)
}
public func removeReactionFromFileComment(name: String, fileComment: String, success: SuccessClosure?, failure: FailureClosure?) {
removeReaction(name: name, fileComment: fileComment, success: success, failure: failure)
}
private func removeReaction(
name: String,
file: String? = nil,
fileComment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
react(.reactionsRemove, name: name, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func react(
_ endpoint: Endpoint,
name: String,
file: String? = nil,
fileComment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"name": name,
"file": file,
"file_comment": fileComment,
"channel": channel,
"timestamp": timestamp
]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
private enum ReactionItemType: String {
case file, comment, message
}
public func getReactionsForFile(_ file: String, full: Bool = true, reactions: (([Reaction]) -> Void)?, failure: FailureClosure?) {
getReactionsForItem(file, full: full, type: .file, reactions: reactions, failure: failure)
}
public func getReactionsForComment(_ comment: String, full: Bool = true, reactions: (([Reaction]) -> Void)?, failure: FailureClosure?) {
getReactionsForItem(comment: comment, full: full, type: .comment, reactions: reactions, failure: failure)
}
public func getReactionsForMessage(
_ channel: String,
timestamp: String,
full: Bool = true,
reactions: (([Reaction]) -> Void)?,
failure: FailureClosure?
) {
getReactionsForItem(channel: channel, timestamp: timestamp, full: full, type: .message, reactions: reactions, failure: failure)
}
private func getReactionsForItem(
_ file: String? = nil,
comment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
full: Bool,
type: ReactionItemType,
reactions: (([Reaction]) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"file": file,
"file_comment": comment,
"channel": channel,
"timestamp": timestamp,
"full": full
]
networkInterface.request(.reactionsGet, accessToken: token, parameters: parameters, successClosure: {(response) in
guard let item = response[type.rawValue] as? [String: Any] else {
reactions?([])
return
}
switch type {
case .message:
let message = Message(dictionary: item)
reactions?(message.reactions)
case .file:
let file = File(file: item)
reactions?(file.reactions)
case .comment:
let comment = Comment(comment: item)
reactions?(comment.reactions)
}
}) {(error) in
failure?(error)
}
}
public func reactionsListForUser(
_ user: String? = nil,
full: Bool = true,
count: Int = 100,
page: Int = 1,
success: ItemsClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"user": user,
"full": full,
"count": count,
"page": page
]
networkInterface.request(.reactionsList, accessToken: token, parameters: parameters, successClosure: {(response) in
let items = response["items"] as? [[String: Any]]
success?(items?.map({ Item(item: $0) }))
}) {(error) in
failure?(error)
}
}
}
// MARK: - Stars
extension WebAPI {
public func addStarToChannel(channel: String, success: SuccessClosure?, failure: FailureClosure?) {
addStar(channel: channel, success: success, failure: failure)
}
public func addStarToMessage(channel: String, timestamp: String, success: SuccessClosure?, failure: FailureClosure?) {
addStar(channel: channel, timestamp: timestamp, success: success, failure: failure)
}
public func addStarToFile(file: String, success: SuccessClosure?, failure: FailureClosure?) {
addStar(file: file, success: success, failure: failure)
}
public func addStarToFileComment(fileComment: String, success: SuccessClosure?, failure: FailureClosure?) {
addStar(fileComment: fileComment, success: success, failure: failure)
}
private func addStar(
file: String? = nil,
fileComment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
star(.starsAdd, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
public func removeStarFromChannel(channel: String, success: SuccessClosure?, failure: FailureClosure?) {
removeStar(channel: channel, success: success, failure: failure)
}
public func removeStarFromMessage(channel: String, timestamp: String, success: SuccessClosure?, failure: FailureClosure?) {
removeStar(channel: channel, timestamp: timestamp, success: success, failure: failure)
}
public func removeStarFromFile(file: String, success: SuccessClosure?, failure: FailureClosure?) {
removeStar(file: file, success: success, failure: failure)
}
public func removeStarFromFilecomment(fileComment: String, success: SuccessClosure?, failure: FailureClosure?) {
removeStar(fileComment: fileComment, success: success, failure: failure)
}
private func removeStar(
file: String? = nil,
fileComment: String? = nil,
channel: String? = nil,
timestamp: String? = nil,
success: SuccessClosure?,
failure: FailureClosure?
) {
star(.starsRemove, file: file, fileComment: fileComment, channel: channel, timestamp: timestamp, success: {(ok) in
success?(ok)
}) {(error) in
failure?(error)
}
}
private func star(
_ endpoint: Endpoint,
file: String?,
fileComment: String?,
channel: String?,
timestamp: String?,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"file": file,
"file_comment": fileComment,
"channel": channel,
"timestamp": timestamp
]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Team
extension WebAPI {
public func teamInfo(success: ((_ info: [String: Any]?) -> Void)?, failure: FailureClosure?) {
networkInterface.request(.teamInfo, accessToken: token, parameters: [:], successClosure: {(response) in
success?(response["team"] as? [String: Any])
}) {(error) in
failure?(error)
}
}
}
// MARK: - Users
extension WebAPI {
public func userConversations(
cursor: String? = nil,
excludeArchived: Bool? = nil,
limit: Int? = nil,
types: [ConversationType]? = nil,
userID: String? = nil,
success: ChannelsClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any?] = [
"cursor": cursor,
"exclude_archived": excludeArchived,
"limit": limit,
"types": types?.map({ $0.rawValue }).joined(separator: ","),
"user": userID
]
networkInterface.request(.usersConversations, accessToken: token, parameters: parameters, successClosure: {(response) in
let channels: [Channel] = (response["channels"] as? [[String: Any]])?.map{Channel(channel: $0)} ?? []
success?(channels, (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func userPresence(user: String, success: ((_ presence: String?) -> Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user": user]
networkInterface.request(.usersGetPresence, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["presence"] as? String)
}) {(error) in
failure?(error)
}
}
public func userInfo(id: String, success: ((_ user: User) -> Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["user": id]
networkInterface.request(.usersInfo, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(User(user: response["user"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
public func usersList(cursor: String? = nil,
limit: Int? = nil,
includePresence: Bool = false,
success: ((_ userList: [[String: Any]]?, _ nextCursor: String?) -> Void)?,
failure: FailureClosure?) {
var parameters: [String: Any] = ["presence": includePresence]
if let cursor = cursor {
parameters["cursor"] = cursor
}
if let limit = limit {
parameters["limit"] = limit
}
networkInterface.request(.usersList, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["members"] as? [[String: Any]], (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func usersLookupByEmail(_ email: String, success: ((_ user: User) -> Void)?, failure: FailureClosure?) {
let parameters: [String: Any] = ["email": email]
networkInterface.request(.usersLookupByEmail, accessToken: token, parameters: parameters, successClosure: { response in
success?(User(user: response["user"] as? [String: Any]))
}) { error in
failure?(error)
}
}
public func usersProfileSet(profile: User.Profile, success: SuccessClosure?, failure: FailureClosure?) {
let profileValues = ([
"first_name": profile.firstName,
"last_name": profile.lastName,
"real_name": profile.realName,
"email": profile.email,
"phone": profile.phone,
"status_text": profile.statusText,
"status_emoji": profile.statusEmoji,
"status_expiration": profile.statusExpiration
] as [String: Any?])
.filter { $0.value != nil }
.mapValues { $0! }
do {
let data = try JSONSerialization.data(withJSONObject: ["profile": profileValues])
let urlComponents = URLComponents(string: "https://slack.com/api/users.profile.set")
guard let requestString = urlComponents?.url?.absoluteString else {
throw SlackError.clientNetworkError
}
networkInterface.customRequest(requestString, token: token, data: data, success: { _ in
success?(true)
}) {(error) in
failure?(error)
}
} catch {
failure?(error as? SlackError ?? SlackError.unknownError)
}
}
public func setUserActive(success: SuccessClosure?, failure: FailureClosure?) {
networkInterface.request(.usersSetActive, accessToken: token, parameters: [:], successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
public func setUserPresence(presence: Presence, success: SuccessClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["presence": presence.rawValue]
networkInterface.request(.usersSetPresence, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Conversations
extension WebAPI {
public func conversationsArchive(channel: String, success: (() -> Void)?, failure: FailureClosure?) {
let parameters = ["channel": channel]
networkInterface.request(.conversationsArchive, accessToken: token, parameters: parameters, successClosure: {_ in }) {(error) in
failure?(error)
}
}
public func conversationsCreate(
name: String,
isPrivate: Bool = false,
success: ((_ id: String?, _ name: String?, _ creator: String?) -> Void)?,
failure: FailureClosure?
) {
let parameters = [
"name": name,
"is_private": isPrivate
] as [String : Any]
networkInterface.request(.conversationsOpen, accessToken: token, parameters: parameters, successClosure: {(response) in
let group = response["channel"] as? [String: Any]
success?(
group?["id"] as? String,
group?["name"] as? String,
group?["creator"] as? String
)
}) {(error) in
failure?(error)
}
}
public func conversationsList(
excludeArchived: Bool = false,
cursor: String? = nil,
limit: Int? = nil,
types: [ConversationType]? = nil,
success: ((_ channels: [[String: Any]]?, _ nextCursor: String?) -> Void)?,
failure: FailureClosure?
) {
var parameters: [String: Any] = ["exclude_archived": excludeArchived]
if let cursor = cursor {
parameters["cursor"] = cursor
}
if let limit = limit {
parameters["limit"] = limit
}
if let types = types {
parameters["types"] = types.map({ $0.rawValue }).joined(separator: ",")
}
networkInterface.request(.conversationsList, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["channels"] as? [[String: Any]], (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func conversationsReplies(
id: String,
ts: String,
cursor: String? = nil,
inclusive: Bool = false,
latest: String = "\(Date().timeIntervalSince1970)",
limit: Int = 10,
oldest: String = "0",
success: ((_ channels: [[String: Any]]?, _ nextCursor: String?) -> Void)?,
failure: FailureClosure?
) {
var parameters: [String: Any] = [
"channel": id,
"ts": ts,
"inclusive": inclusive,
"limit": limit,
"latest": latest,
"oldest": oldest,
]
if let cursor = cursor {
parameters["cursor"] = cursor
}
networkInterface.request(.conversationsReplies, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["messages"] as? [[String: Any]], (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func conversationsMembers(
id: String,
cursor: String? = nil,
limit: Int? = nil,
success: ((_ members: [String]?, _ nextCursor: String?) -> Void)?,
failure: FailureClosure?
) {
var parameters: [String: Any] = [
"channel": id
]
if let cursor = cursor {
parameters["cursor"] = cursor
}
if let limit = limit {
parameters["limit"] = limit
}
networkInterface.request(.conversationsMembers, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["members"] as? [String], (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func conversationsHistory(
id: String,
cursor: String? = nil,
inclusive: Bool = false,
latest: String = "\(Date().timeIntervalSince1970)",
limit: Int = 10,
oldest: String = "0",
success: ((_ channels: [[String: Any]]?, _ nextCursor: String?) -> Void)?,
failure: FailureClosure?
) {
var parameters: [String: Any] = [
"channel": id,
"inclusive": inclusive,
"limit": limit,
"latest": latest,
"oldest": oldest,
]
if let cursor = cursor {
parameters["cursor"] = cursor
}
networkInterface.request(.conversationsHistory, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response["messages"] as? [[String: Any]], (response["response_metadata"] as? [String: Any])?["next_cursor"] as? String)
}) {(error) in
failure?(error)
}
}
public func conversationsOpen(
userIDs: [String],
success: ((_ imID: String?) -> Void)?,
failure: FailureClosure?
) {
let parameters = [
"users": userIDs.joined(separator: ",")
]
networkInterface.request(.conversationsOpen, accessToken: token, parameters: parameters, successClosure: {(response) in
let group = response["channel"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Search
extension WebAPI {
public enum SearchSort: String {
case score
case timestamp
}
public enum SearchSortDirection: String {
case asc
case desc
}
public func search(
query: String,
count: Int = 20,
highlight: Bool = false,
page: Int = 1,
sort: SearchSort = .score,
sortDir: SearchSortDirection = .desc,
success: ((_ files: [[String : Any]]?, _ messages: [[String : Any]]?) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any] = [
"query": query,
"count": count,
"highlight": highlight,
"page": page,
"sort": sort.rawValue,
"sort_dir": sortDir.rawValue,
]
networkInterface.request(.searchAll, accessToken: token, parameters: parameters, successClosure: { (response) in
success?(
(response["files"] as? [String : Any])?["matches"] as? [[String : Any]],
(response["messages"] as? [String : Any])?["matches"] as? [[String : Any]]
)
}) { (error) in
failure?(error)
}
}
public func searchFiles(
query: String,
count: Int = 20,
highlight: Bool = false,
page: Int = 1,
sort: SearchSort = .score,
sortDir: SearchSortDirection = .desc,
success: ((_ files: [[String : Any]]?) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any] = [
"query": query,
"count": count,
"highlight": highlight,
"page": page,
"sort": sort.rawValue,
"sort_dir": sortDir.rawValue,
]
networkInterface.request(.searchFiles, accessToken: token, parameters: parameters, successClosure: { (response) in
success?(
(response["files"] as? [String : Any])?["matches"] as? [[String : Any]]
)
}) { (error) in
failure?(error)
}
}
public func searchMessages(
query: String,
count: Int = 20,
highlight: Bool = false,
page: Int = 1,
sort: SearchSort = .score,
sortDir: SearchSortDirection = .desc,
success: ((_ messages: [[String : Any]]?) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any] = [
"query": query,
"count": count,
"highlight": highlight,
"page": page,
"sort": sort.rawValue,
"sort_dir": sortDir.rawValue,
]
networkInterface.request(.searchMessages, accessToken: token, parameters: parameters, successClosure: { (response) in
success?(
(response["messages"] as? [String : Any])?["matches"] as? [[String : Any]]
)
}) { (error) in
failure?(error)
}
}
}
// MARK: - Utilities
extension WebAPI {
fileprivate func encodeAttachments(_ attachments: [Attachment?]?) -> String? {
if let attachments = attachments {
var attachmentArray: [[String: Any]] = []
for attachment in attachments {
if let attachment = attachment {
attachmentArray.append(attachment.dictionary)
}
}
do {
let data = try JSONSerialization.data(withJSONObject: attachmentArray, options: [])
return String(data: data, encoding: String.Encoding.utf8)
} catch let error {
print(error)
}
}
return nil
}
fileprivate func encodeBlocks(_ blocks: [Block]?) -> String? {
if let blocks = blocks {
let blocksArray: [[String: Any]] = blocks.map { $0.dictionary }
do {
let data = try JSONSerialization.data(withJSONObject: blocksArray, options: [])
return String(data: data, encoding: String.Encoding.utf8)
} catch let error {
print(error)
}
}
return nil
}
fileprivate func enumerateDNDStatuses(_ statuses: [String: Any]) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
for key in statuses.keys {
retVal[key] = DoNotDisturbStatus(status: statuses[key] as? [String: Any])
}
return retVal
}
fileprivate func close(_ endpoint: Endpoint, channelID: String, success: SuccessClosure?, failure: FailureClosure?) {
let parameters: [String: Any] = ["channel": channelID]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func history(
_ endpoint: Endpoint,
id: String,
latest: String = "\(Date().timeIntervalSince1970)",
oldest: String = "0",
inclusive: Bool = false,
count: Int = 100,
unreads: Bool = false,
success: HistoryClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = [
"channel": id,
"latest": latest,
"oldest": oldest,
"inclusive": inclusive,
"count": count,
"unreads": unreads
]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(History(history: response))
}) {(error) in
failure?(error)
}
}
fileprivate func info(
_ endpoint: Endpoint,
type: ChannelType,
id: String,
success: ChannelClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": id]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(Channel(channel: response[type.rawValue] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
fileprivate func list(
_ endpoint: Endpoint,
type: ChannelType,
excludeArchived: Bool = false,
excludeMembers: Bool = false,
success: ((_ channels: [[String: Any]]?) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["exclude_archived": excludeArchived, "exclude_members": excludeMembers]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(response[type.rawValue+"s"] as? [[String: Any]])
}) {(error) in
failure?(error)
}
}
fileprivate func mark(
_ endpoint: Endpoint,
channel: String,
timestamp: String,
success: ((_ ts: String) -> Void)?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel, "ts": timestamp]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(timestamp)
}) {(error) in
failure?(error)
}
}
fileprivate func setInfo(
_ endpoint: Endpoint,
type: InfoType,
channel: String,
text: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel, type.rawValue: text]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func create(
_ endpoint: Endpoint,
name: String,
success: ChannelClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["name": name]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: {(response) in
success?(Channel(channel: response["channel"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
fileprivate func invite(
_ endpoint: Endpoint,
channel: String,
user: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel, "user": user]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func join(
_ endpoint: Endpoint,
name: String,
validate: Bool,
success: ChannelClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["name": name, "validate": validate]
networkInterface.request(endpoint, accessToken: token, parameters: parameters, successClosure: { response in
success?(Channel(channel: response["channel"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
fileprivate func leave(
_ endpoint: Endpoint,
channel: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel]
networkInterface.request(endpoint, accessToken: token, parameters: parameters,successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func archive(
_ endpoint: Endpoint,
channel: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel]
networkInterface.request(endpoint, accessToken: token, parameters: parameters,successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func unarchive(
_ endpoint: Endpoint,
channel: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel]
networkInterface.request(endpoint, accessToken: token, parameters: parameters,successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
fileprivate func rename(
_ endpoint: Endpoint,
channel: String,
name: String,
validate: Bool,
success: ChannelClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel, "name": name, "validate": validate ]
networkInterface.request(endpoint, accessToken: token, parameters: parameters,successClosure: { response in
success?(Channel(channel: response["channel"] as? [String: Any]))
}) {(error) in
failure?(error)
}
}
fileprivate func kick(
_ endpoint: Endpoint,
channel: String,
user: String,
success: SuccessClosure?,
failure: FailureClosure?
) {
let parameters: [String: Any] = ["channel": channel, "user": user]
networkInterface.request(endpoint, accessToken: token, parameters: parameters,successClosure: { _ in
success?(true)
}) {(error) in
failure?(error)
}
}
}
// MARK: - Deprecated
extension WebAPI {
// MARK: channels.*
@available(*, deprecated, message: "Use conversationsArchive instead.")
public func channelsArchive(_ channel: String, success: SuccessClosure?, failure: FailureClosure?) {
archive(.channelsArchive, channel: channel, success: success, failure: failure)
}
@available(*, deprecated, message: "Use conversationsCreate instead.")
public func createChannel(channel: String, success: ChannelClosure?, failure: FailureClosure?) {
create(.channelsCreate, name: channel, success: success, failure: failure)
}
@available(*, deprecated)
public func channelInfo(id: String, success: ChannelClosure?, failure: FailureClosure?) {
info(.channelsInfo, type:.channel, id: id, success: {(channel) in
success?(channel)
}) {(error) in
failure?(error)
}
}
@available(*, deprecated)
public func inviteToChannel(_ channel: String, user: String, success: SuccessClosure?, failure: FailureClosure?) {
invite(.channelsInvite, channel: channel, user: user, success: success, failure: failure)
}
@available(*, deprecated)
public func channelsJoin(_ name: String, validate: Bool, success: ChannelClosure?, failure: FailureClosure?) {
join(.channelsJoin, name: name, validate: validate, success: success, failure: failure)
}
@available(*, deprecated)
public func channelsList(
excludeArchived: Bool = false,
excludeMembers: Bool = false,
success: ((_ channels: [[String: Any]]?) -> Void)?,
failure: FailureClosure?
) {
list(.channelsList, type:.channel, excludeArchived: excludeArchived, excludeMembers: excludeMembers, success: {(channels) in
success?(channels)
}) {(error) in
failure?(error)
}
}
@available(*, deprecated)
public func markChannel(channel: String, timestamp: String, success: ((_ ts: String) -> Void)?, failure: FailureClosure?) {
mark(.channelsMark, channel: channel, timestamp: timestamp, success: {(ts) in
success?(ts)
}) {(error) in
failure?(error)
}
}
// MARK: im.*
@available(*, deprecated, message: "Use conversationsOpen instead.")
public func openIM(userID: String, success: ((_ imID: String?) -> Void)?, failure: FailureClosure?) {
let parameters = ["user": userID]
networkInterface.request(.imOpen, accessToken: token, parameters: parameters, successClosure: {(response) in
let group = response["channel"] as? [String: Any]
success?(group?["id"] as? String)
}) {(error) in
failure?(error)
}
}
}