Skip to content

Commit 1613575

Browse files
2 parents 302d9e8 + c39a77c commit 1613575

27 files changed

+854
-704
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tools/telegram.sh
77
deploy.sh
88

99
**/geckodriver.log
10-
Notifier
10+
./Notifier
1111
permissions.txt
1212
urlwatcher/urls.list
1313
urlwatcher/images/*

NotifierBot/Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ let package = Package(
1414
dependencies: [
1515
// Dependencies declare other packages that this package depends on.
1616
// .package(url: /* package url */, from: "1.0.0"),
17-
.package(url: "https://github.com/rapierorg/telegram-bot-swift.git", .exact("1.1.0"))
17+
.package(url: "https://github.com/givip/Telegrammer", from: "0.5.4")
1818
],
1919
targets: [
2020
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2121
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
2222
.target(
2323
name: "Notifier",
24-
dependencies: ["TelegramBotSDK"]),
24+
dependencies: ["Telegrammer"]),
2525
.testTarget(
2626
name: "NotifierTests",
2727
dependencies: ["Notifier"]),
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// BotPermission.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 14.06.20.
6+
//
7+
8+
import Foundation
9+
10+
enum BotPermission: String, Comparable, CaseIterable {
11+
case user = "user"
12+
case mod = "mod"
13+
case admin = "admin"
14+
15+
static private let levels: [BotPermission: Int] = [
16+
.user: 0,
17+
.mod: 1,
18+
.admin: 2
19+
]
20+
21+
static func <(lhs: BotPermission, rhs: BotPermission) -> Bool {
22+
return levels[lhs]! < levels[rhs]!
23+
}
24+
25+
static func >(lhs: BotPermission, rhs: BotPermission) -> Bool {
26+
return levels[lhs]! > levels[rhs]!
27+
}
28+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//
2+
// AddCommand.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 01.05.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
struct AddCommand: Command {
12+
13+
let name = "Add"
14+
let commands = ["/add"]
15+
let syntax = "/add <name> <URL> \\[x y width height]"
16+
let description = "Adds a new website with an optional screenshot area to the list"
17+
let permission = BotPermission.mod
18+
19+
func run(update: Update, context: BotContext?) throws {
20+
let chatID = try update.chatID()
21+
let args = try update.args()
22+
guard args.count > 0 else {
23+
try showUsage(chatID)
24+
return
25+
}
26+
27+
// Check if there is a valid URL
28+
guard let urlIndex = args.lastIndex(where: { $0.starts(with: "http") }) else {
29+
// No valid URL found
30+
try bot.sendMessage("Please enter a valid URL, starting with 'http://' or 'https://'", to: chatID)
31+
return
32+
}
33+
// The first argument should be the name, not the URL
34+
if urlIndex == 0 {
35+
try showUsage(chatID)
36+
return
37+
}
38+
// Parse the arguments
39+
let name = args[0..<urlIndex].joined(separator: " ")
40+
let url = args[urlIndex]
41+
42+
let area: Rectangle!
43+
if args.count == urlIndex + 5 {
44+
// If a cropping area was supplied
45+
let x = Int(args[urlIndex + 1])
46+
let y = Int(args[urlIndex + 2])
47+
let width = Int(args[urlIndex + 3])
48+
let height = Int(args[urlIndex + 4])
49+
guard x != nil && y != nil && width != nil && height != nil else {
50+
try bot.sendMessage("Please enter a valid Offset and Size", to: chatID)
51+
return
52+
}
53+
area = Rectangle(x: x!, y: y!, width: width!, height: height!)
54+
} else if args.count == urlIndex + 1 {
55+
// No cropping area
56+
area = .zero
57+
} else {
58+
try showUsage(chatID)
59+
return
60+
}
61+
62+
// Create the new entry
63+
let entry = URLEntry(name: name, url: url, area: area, chatID: chatID)
64+
var config = try ConfigParser.getConfig()
65+
// Only check for matching names in the same chat
66+
guard !config.contains(where: { $0.name.lowercased() == name.lowercased() && $0.chatID == chatID }) else {
67+
try bot.sendMessage("There is an entry with that name already", to: chatID)
68+
return
69+
}
70+
config.append(entry)
71+
try ConfigParser.saveConfig(config)
72+
73+
try bot.sendMessage("Added '\(name)'", to: chatID)
74+
}
75+
76+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// CheckCommand.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 03.05.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
struct CheckCommand: Command {
12+
13+
let name = "Check"
14+
let commands = ["/check"]
15+
let syntax = "/check"
16+
let description = "Performs a manual check if any monitored website changed"
17+
let permission = BotPermission.admin
18+
19+
func run(update: Update, context: BotContext?) throws {
20+
let chatID = try update.chatID()
21+
let args = try update.args()
22+
guard args.count == 0 else {
23+
try showUsage(chatID)
24+
return
25+
}
26+
try bot.sendMessage("Starting check...", to: chatID)
27+
// TODO: Do async?
28+
JFUtils.shell("\(kUrlwatchTool)")
29+
try bot.sendMessage("Check complete", to: chatID)
30+
}
31+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// Command.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 30.04.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
protocol Command {
12+
13+
var name: String { get }
14+
var commands: [String] { get }
15+
var permission: BotPermission { get }
16+
var syntax: String { get }
17+
var usage: String { get }
18+
var description: String { get }
19+
20+
var handler: Handler { get }
21+
22+
func run(update: Update, context: BotContext?) throws
23+
24+
func showUsage(_ chatID: Int64) throws
25+
}
26+
27+
extension Command {
28+
29+
var usage: String {
30+
return "Usage: " + syntax
31+
}
32+
33+
var handler: Handler {
34+
CommandHandler(name: name, commands: commands, callback: { (update, context) in
35+
do {
36+
// Check if the sender is a user
37+
guard let userID = update.message?.from?.id else {
38+
throw JFBotError.noUserID
39+
}
40+
// Check if the user has the required permissions
41+
guard PermissionHandler.shared.hasPermission(userID: userID, permission: self.permission) else {
42+
throw JFBotError.noPermissions(self)
43+
}
44+
// Run the command
45+
try self.run(update: update, context: context)
46+
} catch let error as JFBotError {
47+
JFErrorHandler.shared.handle(error, update: update)
48+
}
49+
})
50+
}
51+
52+
func showUsage(_ chatID: Int64) throws {
53+
try bot.sendMessage(usage, to: chatID)
54+
}
55+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// FetchCommand.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 03.05.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
struct FetchCommand: Command {
12+
13+
let name = "Fetch"
14+
let commands = ["/fetch"]
15+
let syntax = "/fetch <name>"
16+
let description = "Takes a screenshot with the stored settings and sends it into this chat"
17+
let permission = BotPermission.mod
18+
19+
func run(update: Update, context: BotContext?) throws {
20+
let chatID = try update.chatID()
21+
let args = try update.args()
22+
guard args.count > 0 else {
23+
try showUsage(chatID)
24+
return
25+
}
26+
let name = args.joined(separator: " ")
27+
// Get the settings
28+
let config = try ConfigParser.getConfig()
29+
let entry = config.first(where: { $0.name.lowercased() == name.lowercased() && $0.chatID == chatID })
30+
guard entry != nil else {
31+
try bot.sendMessage("There is no entry with the name '\(name)'", to: chatID)
32+
return
33+
}
34+
DispatchQueue.main.async {
35+
// Take the screenshot
36+
JFUtils.takeScreenshot(url: entry!.url, filename: "/tmp/screenshot.png", area: entry!.area)
37+
// Send the screenshot as file
38+
// Use the script, because its easier than sending the file in swift
39+
JFUtils.sendFile(path: "/tmp/screenshot.png", chatID: chatID)
40+
}
41+
}
42+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 03.05.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
struct FetchURLCommand: Command {
12+
13+
let name = "Fetch URL"
14+
let commands = ["/fetchurl"]
15+
let syntax = "/fetchurl <URL> \\[x y width height]"
16+
let description = "Takes a screenshot of the given website and settings and sends it into this chat"
17+
let permission = BotPermission.mod
18+
19+
func run(update: Update, context: BotContext?) throws {
20+
let chatID = try update.chatID()
21+
let args = try update.args()
22+
guard args.count == 1 || args.count == 5 else {
23+
try showUsage(chatID)
24+
return
25+
}
26+
let url = args[0]
27+
var area: Rectangle = .zero
28+
if args.count == 5 {
29+
let x = Int(args[1])
30+
let y = Int(args[2])
31+
let width = Int(args[3])
32+
let height = Int(args[4])
33+
guard x != nil && y != nil && width != nil && height != nil else {
34+
try bot.sendMessage("Please enter a valid Offset and Size", to: chatID)
35+
return
36+
}
37+
area = Rectangle(x: x!, y: y!, width: width!, height: height!)
38+
}
39+
guard url.hasPrefix("http") else {
40+
try bot.sendMessage("Please enter a valid URL, starting with 'http://' or 'https://'", to: chatID)
41+
return
42+
}
43+
DispatchQueue.main.async {
44+
JFUtils.takeScreenshot(url: url, filename: "/tmp/screenshot.png", area: area)
45+
JFUtils.sendFile(path: "/tmp/screenshot.png", chatID: chatID)
46+
}
47+
}
48+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// GetPermissionsCommand.swift
3+
//
4+
//
5+
// Created by Jonas Frey on 03.05.21.
6+
//
7+
8+
import Foundation
9+
import Telegrammer
10+
11+
struct GetPermissionsCommand: Command {
12+
13+
let name = "Get Permissions"
14+
let commands = ["/getpermissions"]
15+
let syntax = "/getpermissions \\[id]"
16+
let description = "Returns the permission level of the author of the message, replied to or the user id provided"
17+
let permission = BotPermission.admin
18+
19+
func run(update: Update, context: BotContext?) throws {
20+
let chatID = try update.chatID()
21+
let args = try update.args()
22+
guard args.count <= 1 else {
23+
try showUsage(chatID)
24+
return
25+
}
26+
27+
// TODO: Check if it works with mentions
28+
let _ = update.message?.entities
29+
30+
let userID: Int64!
31+
let username: String!
32+
if args.count == 0 {
33+
// Use the reply message
34+
guard let user = update.message?.replyToMessage?.from, !user.isBot else {
35+
try bot.sendMessage("Error: Please respond to a message of a user.", to: chatID)
36+
return
37+
}
38+
userID = user.id
39+
username = user.username ?? "<Unknown>"
40+
} else {
41+
// Use the ID provided
42+
guard let id = Int64(args[0].trimmingCharacters(in: .whitespaces)) else {
43+
try bot.sendMessage("Error: Please provide the user ID as an integer.", to: chatID)
44+
return
45+
}
46+
userID = id
47+
username = "\(id)"
48+
}
49+
let level = ConfigParser.shared.permissionGroup(user: userID)
50+
try bot.sendMessage("The permission level of \(username!) is *\(level.rawValue)*.", to: chatID)
51+
}
52+
}

0 commit comments

Comments
 (0)