Skip to content

Commit 34b2010

Browse files
committed
Update camille and services to new Chameleon framework
1 parent 3199a0a commit 34b2010

11 files changed

+296
-252
lines changed

Package.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1+
// swift-tools-version:3.1
2+
13
import PackageDescription
24

35
let package = Package(
46
name: "Camille",
5-
targets: [
6-
Target(
7-
name: "Camille",
8-
dependencies: []
9-
)
10-
],
117
dependencies: [
12-
.Package(url: "https://github.com/ChameleonBot/Bot.git", majorVersion: 0, minor: 2),
13-
.Package(url: "https://github.com/ChameleonBot/Sugar.git", majorVersion: 0, minor: 2)
8+
.Package(url: "https://github.com/ChameleonBot/Chameleon.git", majorVersion: 1),
149
],
1510
exclude: [
1611
"XcodeProject"
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import Sugar
2-
3-
extension Configs {
4-
static let Karma = KarmaService.Config(
5-
topUsersLimit: 20,
6-
karmaAdjusters: [("++", 1), ("--", -1)],
7-
textDistanceThreshold: 4,
8-
allowedBufferCharacters: [" ", ":"],
9-
positiveMessage: { user, total in
10-
return ["\(user.name) you rock!: \(total)"]
11-
},
12-
negativeMessage: { user, total in
13-
return ["Boooo \(user.name)!: \(total)"]
14-
}
15-
)
16-
}
1+
//import Sugar
2+
//
3+
//extension Configs {
4+
// static let Karma = KarmaService.Config(
5+
// topUsersLimit: 20,
6+
// karmaAdjusters: [("++", 1), ("--", -1)],
7+
// textDistanceThreshold: 4,
8+
// allowedBufferCharacters: [" ", ":"],
9+
// positiveMessage: { user, total in
10+
// return ["\(user.name) you rock!: \(total)"]
11+
// },
12+
// negativeMessage: { user, total in
13+
// return ["Boooo \(user.name)!: \(total)"]
14+
// }
15+
// )
16+
//}

Sources/Camille/HelloService.swift

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
1-
import Bot
2-
import Sugar
1+
import Chameleon
32

4-
final class HelloService: SlackMessageService {
5-
func messageEvent(slackBot: SlackBot, webApi: WebAPI, message: MessageDecorator, previous: MessageDecorator?) throws {
6-
guard let target = message.target, let sender = message.sender else { return }
7-
8-
try message.routeText(
9-
to: self.sayHello(to: sender, in: target, with: webApi),
10-
matching: Greeting(name: "greeting"), slackBot.me
11-
)
3+
final class HelloService: SlackBotMessageService {
4+
func configure(slackBot: SlackBot) {
5+
configureMessageService(slackBot: slackBot)
6+
7+
slackBot.registerHelp(item: Patterns.greeting(slackBot))
8+
}
9+
func onMessage(slackBot: SlackBot, message: MessageDecorator, previous: MessageDecorator?) throws {
10+
try slackBot.route(message, matching: Patterns.greeting(slackBot), to: sendGreeting)
11+
}
12+
13+
private func sendGreeting(bot: SlackBot, message: MessageDecorator, match: PatternMatch) throws -> Void {
14+
let response = try message
15+
.respond()
16+
.text(["well", try match.value(key: "greeting"), "back at you", try message.sender()])
17+
.makeChatMessage()
18+
19+
try bot.send(response)
1220
}
1321
}
1422

15-
fileprivate extension HelloService {
16-
func sayHello(to sender: User, in target: SlackTargetType, with webApi: WebAPI) -> (PatternMatchResult) throws -> Void {
17-
return { match in
18-
let message = try SlackMessage()
19-
.line(match.value(named: "greeting"), " ", sender)
20-
.makeChatPostMessage(target: target)
21-
22-
try webApi.execute(message)
23+
private enum Patterns: HelpRepresentable {
24+
case greeting(SlackBot)
25+
26+
var topic: String {
27+
return "Greetings"
28+
}
29+
var description: String {
30+
return "Greet the bot"
31+
}
32+
33+
var pattern: [Matcher] {
34+
switch self {
35+
case .greeting(let bot): return [["hey", "hi", "hello"].any.using(key: "greeting"), bot.me]
2336
}
2437
}
2538
}

Sources/Camille/Karma/KarmaAdjustable.swift

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 63 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,71 @@
1-
import Bot
2-
import Sugar
1+
import Chameleon
2+
import Foundation
3+
4+
private typealias PartialUpdate = (user: ModelPointer<User>, operation: Operation)
5+
private typealias Update = (ModelPointer<User>, (current: Int, change: Int))
6+
7+
private enum Operation: String {
8+
case plus = "++"
9+
case minus = "--"
10+
11+
func update(value: Int) -> Int {
12+
switch self {
13+
case .plus: return value + 1
14+
case .minus: return value - 1
15+
}
16+
}
17+
}
318

419
extension KarmaService {
5-
func adjustKarma(
6-
in message: MessageDecorator,
7-
from sender: User,
8-
in target: SlackTargetType,
9-
storage: Storage,
10-
webApi: WebAPI) throws -> Void {
11-
12-
//find any karma adjustments
13-
let adjustments: [(user: User, amount: Int)] = message.mentioned_users.flatMap { link in
14-
//user can't adjust their own karma
15-
guard link.value != sender else { return nil }
16-
17-
//Move past the `>` and get the rest of the message
18-
let start = message.text.index(after: link.range.upperBound)
19-
let text = message.text.substring(from: start)
20-
21-
//iterate forward until we pass the threshold or hit the end of the string
22-
for index in (0..<min(self.config.textDistanceThreshold, text.characters.count)) {
23-
//check for adjustments
24-
for adjuster in self.config.karmaAdjusters {
25-
if text.substring(from: index).hasPrefix(adjuster.adjuster.karmaValue) {
26-
return (link.value, adjuster.amount)
27-
}
28-
}
29-
30-
//check the allowed chars
31-
let nextChar = Character(text.substring(to: 1))
32-
guard self.config.allowedBufferCharacters.contains(nextChar) else { break }
33-
}
34-
35-
return nil
20+
func adjust(bot: SlackBot, message: MessageDecorator) throws {
21+
guard !message.isIM else { return }
22+
23+
func partialUpdate(from link: MessageDecorator.Link<ModelPointer<User>>) throws -> PartialUpdate? {
24+
guard try link.value.id != message.sender().id else { return nil }
25+
26+
let possibleOperation = message.text
27+
.substring(from: link.range.upperBound)
28+
.trimCharacters(in: [" ", ">", ":"])
29+
.components(separatedBy: " ")
30+
.first ?? ""
31+
32+
guard let operation = Operation(rawValue: possibleOperation)
33+
else { return nil }
34+
35+
return (link.value, operation)
3636
}
37-
38-
guard !adjustments.isEmpty else { return }
39-
40-
//consolidate adjustments
41-
let consolidated: [(user: User, change: Int, total: Int)] = adjustments
42-
.grouped(by: { $0.user })
43-
.flatMap { user, changes in
44-
let current: Int = storage.get(.in("Karma"), key: user.id, or: 0)
45-
let change = changes.reduce(0) { $0 + $1.amount }
46-
guard change != 0 else { return nil }
47-
let newTotal = current + change
48-
return (user, change, newTotal)
37+
38+
func consolidatePartialUpdates(for user: ModelPointer<User>, partials: [PartialUpdate]) throws -> Update {
39+
let count: Int = try storage.get(key: user.id, from: Keys.namespace, or: 0)
40+
let change = partials.reduce(0) { $1.operation.update(value: $0) }
41+
return (user, (count, change))
4942
}
50-
51-
//make adjustments
52-
for adjustment in consolidated where adjustment.change != 0 {
53-
try storage.set(.in("Karma"), key: adjustment.user.id, value: adjustment.total)
43+
44+
let updates = try message
45+
.mentionedUsers
46+
.flatMap(partialUpdate)
47+
.group(by: { $0.user })
48+
.map(consolidatePartialUpdates)
49+
.filter { $0.value.change != 0 }
50+
51+
guard !updates.isEmpty else { return }
52+
53+
let response = try message.respond()
54+
55+
for (user, data) in updates {
56+
let newTotal = data.current + data.change
57+
storage.set(value: newTotal, forKey: user.id, in: Keys.namespace)
58+
59+
let comment = (data.change > 0
60+
? config.positiveComments.randomElement
61+
: config.negativeComments.randomElement
62+
) ?? ""
63+
64+
response
65+
.text([user, comment, newTotal])
66+
.newLine()
5467
}
5568

56-
//build a message
57-
let response = consolidated
58-
.flatMap { adjustment in
59-
if adjustment.change > 0 {
60-
return self.config.positiveMessage(adjustment.user, adjustment.total).randomElement
61-
} else if adjustment.change < 0 {
62-
return self.config.negativeMessage(adjustment.user, adjustment.total).randomElement
63-
}
64-
return nil
65-
}
66-
.joined(separator: "\n")
67-
68-
guard !response.isEmpty else { return }
69-
70-
//respond
71-
let request = ChatPostMessage(target: target, text: response)
72-
try webApi.execute(request)
69+
try bot.send(response.makeChatMessage())
7370
}
7471
}
Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
import Sugar
2-
import Models
31

42
extension KarmaService {
5-
struct Config {
6-
let topUsersLimit: Int
7-
let karmaAdjusters: [(adjuster: KarmaAdjustable, amount: Int)]
8-
let textDistanceThreshold: Int
9-
let allowedBufferCharacters: Set<Character>
10-
let positiveMessage: (_ user: User, _ total: Int) -> [String]
11-
let negativeMessage: (_ user: User, _ total: Int) -> [String]
3+
public struct Config {
4+
let topUserLimit: Int
5+
let positiveComments: [String]
6+
let negativeComments: [String]
7+
8+
public init(topUserLimit: Int, positiveComments: [String], negativeComments: [String]) {
9+
self.topUserLimit = topUserLimit
10+
self.positiveComments = positiveComments
11+
self.negativeComments = negativeComments
12+
}
13+
14+
public static func `default`() -> Config {
15+
return Config(
16+
topUserLimit: 10,
17+
positiveComments: ["you rock!"],
18+
negativeComments: ["booooo!"]
19+
)
20+
}
1221
}
1322
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Chameleon
2+
3+
extension KarmaService {
4+
enum Patterns: HelpRepresentable {
5+
case topUsers
6+
case myCount
7+
case userCount
8+
case adjustment
9+
10+
var topic: String {
11+
return "Karma"
12+
}
13+
var description: String {
14+
switch self {
15+
case .topUsers: return "Find out who's on top!"
16+
case .myCount: return "See how much karma you have"
17+
case .userCount: return "See how much karma someone else has"
18+
case .adjustment: return "Give or take karma from someone"
19+
}
20+
}
21+
22+
var pattern: [Matcher] {
23+
switch self {
24+
case .topUsers: return ["top", Int.any.using(key: Keys.count)]
25+
case .myCount: return ["how much karma do I have"]
26+
case .userCount: return ["how much karma does", User.any.using(key: Keys.user), "have"]
27+
case .adjustment: return [User.any, ["++", "--"].any]
28+
}
29+
}
30+
31+
var strict: Bool { return false }
32+
}
33+
}

0 commit comments

Comments
 (0)