Skip to content

Commit b265a2c

Browse files
committed
Initial commit
0 parents  commit b265a2c

File tree

14 files changed

+682
-0
lines changed

14 files changed

+682
-0
lines changed

.codecov.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ignore:
2+
- Tests

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
.build/
3+
*.xcodeproj
4+
xcuserdata/
5+
.swiftpm/

.swiftlint.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
disabled_rules:
2+
- large_tuple
3+
- identifier_name
4+
5+
excluded:
6+
- Tests
7+
- .build
8+
- .swiftpm

Assets/message.png

29 KB
Loading

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 YR Chen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.resolved

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// swift-tools-version:5.2
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
// swiftlint:disable line_length
5+
import PackageDescription
6+
7+
let package = Package(
8+
name: "swift-log-telegram",
9+
platforms: [
10+
.macOS(.v10_15)
11+
],
12+
products: [
13+
// Products define the executables and libraries produced by a package, and make them visible to other packages.
14+
.library(name: "LoggingTelegram", targets: ["LoggingTelegram"])
15+
],
16+
dependencies: [
17+
// Dependencies declare other packages that this package depends on.
18+
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0")
19+
],
20+
targets: [
21+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
22+
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
23+
.target(
24+
name: "LoggingTelegram",
25+
dependencies: [
26+
.product(name: "Logging", package: "swift-log")
27+
]),
28+
.testTarget(
29+
name: "LoggingTelegramTests",
30+
dependencies: [
31+
.target(name: "LoggingTelegram")
32+
])
33+
]
34+
)

README.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# LoggingTelegram ✈️
2+
3+
![Swift](https://img.shields.io/badge/Swift-5.2-orange.svg)
4+
![Release](https://img.shields.io/github/v/tag/stevapple/swift-log-telegram?label=release&logo=github)
5+
![License](https://img.shields.io/github/license/stevapple/swift-log-telegram)
6+
7+
Welcome to **LoggingTelegram** – a logging backend for [SwiftLog](https://github.com/apple/swift-log) that sends log messages by a [Telegram Bot](https://core.telegram.org/bots). Inspired by and forked from [LoggingSlack](https://github.com/wlisac/swift-log-slack).
8+
9+
## Usage 📝
10+
11+
### Setup Telegram Bot
12+
13+
LoggingTelegram uses [Telegram Bot API](https://core.telegram.org/bots/api) to send log messages to any Telegram chat.
14+
15+
You can chat with [BotFather](https://t.me/BotFather) to create a new bot and get its API token. The config methods are described [here](https://core.telegram.org/bots#6-botfather).
16+
17+
You need to pass the token to `TelegramLogHandler` to set it up.
18+
19+
### Get a Target Chat ID
20+
21+
You can access a chat by its ID. There are various ways to get the ID of a chat, some are discussed in [this thread](https://stackoverflow.com/questions/31078710/how-to-obtain-telegram-chat-id-for-a-specific-user).
22+
23+
You can create a group chat with `TelegramGroup.id(_:Int)`, a channel with `TelegramChannel.id(_:Int)` and a user chat with `TelegramUser.id(_:Int)`.
24+
25+
Alternatively, you can create a channel by its name: `TelegramChannel.name(_:String)`, remember to drop '@' prefix.
26+
27+
### @someone in a Group Chat or a channel
28+
29+
You can mention someone in a group chat or a channel, simply by the various `.mentioning()` APIs, listed below:
30+
31+
```swift
32+
var chat = TelegramChannel.name("test")
33+
34+
// By TelegramUser
35+
let users = [1,2].map { TelegramUser.id($0) }
36+
chat = chat.mentioning(users) // Returns a new TelegramChannel instance
37+
// You can use his username to mention a user
38+
let user = TelegramUser.name("testme")
39+
chat = chat.mentioning(user)
40+
41+
// By TelegramRawId (A more flexible alias of the "TelegramUser" version)
42+
chat = chat.mentioning(.id(3))
43+
chat = chat.mentioning([.name("newtest"), .id(4)])
44+
45+
// By User ID
46+
chat = chat.mentioning(5)
47+
chat = chat.mentioning([6,7,8])
48+
49+
// By Username
50+
chat = chat.mentioning("test 1")
51+
chat = chat.mentioning(["test 2", "test 3"])
52+
```
53+
54+
In brief, the chaining style is recommended to create a chat:
55+
56+
```swift
57+
TelegramChannel.name("test").mentioning([.id(1), .name("2"), .id(3)])
58+
// or
59+
TelegramChannel.name("test").mentioning([1, 3]).mentioning("2")
60+
```
61+
62+
### Bootstrap SwiftLog
63+
64+
LoggingTelegram is intended to be used as a secondary logging backend to send urgent log messages directly to Telegram.
65+
66+
You can use SwiftLog's `MultiplexLogHandler` to setup LoggingTelegram with another logging backend.
67+
68+
```swift
69+
import Logging
70+
import LoggingTelegram
71+
72+
let channel = TelegramChannel.name("test").mentioning([1, 3]).mentioning("2")
73+
74+
LoggingSystem.bootstrap { label in
75+
MultiplexLogHandler([
76+
// Setup TelegramLogHandler with your API Token and Chat instance
77+
TelegramLogHandler(label: label, token: "Your Bot Token Here", chat: channel),
78+
79+
// Setup the standard logging backend to enable console logging
80+
StreamLogHandler.standardOutput(label: label),
81+
82+
// Setup multiple TelegramLogHandlers
83+
// TelegramLogHandler(label: label, token: "Your (Another) Bot Token Here", chat: TelegramGroup.id(123))
84+
])
85+
}
86+
```
87+
88+
### Using a Logger
89+
90+
You can now use SwiftLog as usual and critical log messages are sent directly to Telegram. Test code as below.
91+
92+
```swift
93+
import Logging
94+
95+
let logger = Logger(label: "com.example.ExampleApp.main")
96+
97+
logger.critical("Oops, something went wrong!")
98+
```
99+
100+
### Logger Output
101+
102+
The logger will send a Telegram message as a bot (if the level matches) and a console message since both logging backends were setup. The example above gives the following outputs:
103+
104+
![Telegram message](Assets/message.png)
105+
106+
```plain
107+
2020-04-07T16:05:25+0800 critical: Oops, something went wrong!
108+
```
109+
110+
### Log Level
111+
112+
Only messages of [log level](https://github.com/apple/swift-log#log-levels) `critical` are sent to Telegram by default.
113+
114+
You can adjust the log level minimal for a specific `TelegramLogHandler` by setting its `logLevel` property, or initializing with a `level` property.
115+
116+
```swift
117+
var handler = TelegramLogHandler(label: "test", token: "Your Bot Token Here", chat: TelegramGroup.id(123), level: .error)
118+
handler.logLevel = .info // Unrecommended! Low log levels may burden the server and abuse the Telegram function.
119+
```
120+
121+
You can change the default for all `TelegramLogHandler`s by changing the value of `telegramLogDefaultLevel`.
122+
123+
```swift
124+
telegramLogDefaultLevel = .error
125+
```
126+
127+
Remember, the `TelegramLogHandler.logLevel` property has a priority to `telegramLogDefaultLevel`.
128+
129+
### Mute Setting
130+
131+
To not disturb subscribers, you may need to mute the message. You can pass a `mute: Bool` to the initializer to make it silent:
132+
133+
```swift
134+
TelegramLogHandler(label: label, token: "Your Bot Token Here", chat: channel, mute: true)
135+
```
136+
137+
## Installation 📦
138+
139+
LoggingTelegram requires Xcode 11 or a Swift 5.2 toolchain with the Swift Package Manager.
140+
141+
Add the LoggingTelegram package as a dependency to your `Package.swift` file.
142+
143+
```swift
144+
.package(url: "https://github.com/stevapple/swift-log-telegram.git", from: "0.0.1")
145+
```
146+
147+
Add LoggingTelegram to your target's dependencies.
148+
149+
```swift
150+
.target(name: "Example", dependencies: ["LoggingTelegram"])
151+
```
152+
153+
## Attention ⚠️
154+
155+
You should know what you're doing though this package. Do not use it at client-side to protect the subscribers' info and your Bot Token.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Foundation
2+
import Logging
3+
4+
typealias Level = Logger.Level
5+
typealias Metadata = Logger.Metadata
6+
typealias Message = Logger.Message
7+
8+
struct MarkdownLog: CustomStringConvertible {
9+
let timestamp: String
10+
let label: String
11+
let level: Level
12+
let message: Message
13+
let metadata: Metadata
14+
let file: String
15+
let function: String
16+
let line: UInt
17+
let mentionedUsers: [TelegramUser]
18+
19+
var description: String {
20+
let title = "[\(label)] [\(level)]"
21+
let location = "\(function) @ \(file):\(line)"
22+
return timestamp.telegramEscaping() + " *\(title.telegramEscaping())*\n"
23+
+ "*\(message.telegramEscaping())*\n"
24+
+ location.telegramEscaping()
25+
+ (metadata.count > 0 ? "*Metadata*\n" : "")
26+
+ metadata.map {"*\($0.telegramEscaping())*: \($1)"} .joined(separator: "\n")
27+
+ (mentionedUsers.count > 0 ? "\n" : "")
28+
+ mentionedUsers.map {"\($0)"} .joined(separator: " ")
29+
}
30+
}
31+
32+
extension String {
33+
func telegramEscaping() -> String {
34+
var text = self
35+
for char in "_*[]()~`>#+-=|{}.!" {
36+
text = text.replacingOccurrences(of: "\(char)", with: "\\\(char)")
37+
}
38+
return text
39+
}
40+
}
41+
42+
extension CustomStringConvertible {
43+
func telegramEscaping() -> String {
44+
return self.description.telegramEscaping()
45+
}
46+
}

0 commit comments

Comments
 (0)