Compare commits

...

8 commits

Author SHA1 Message Date
cffada6b3b
chore: Initialize actions
All checks were successful
Write changelog / write-changelog (push) Successful in 22s
Rust CI / Rust CI (push) Successful in 3m7s
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-29 01:20:19 +03:00
12606f45bc
chore: Add rust files
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:33 +03:00
61a51be953
chore: Add Justfile
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:33 +03:00
2b1546a181
chore: Initialize the changelogs
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:33 +03:00
99b3d5418d
chore: Upgrade dependencies and fix lint errors
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:33 +03:00
9100ebcb11
chore(fmt): Format the code
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:32 +03:00
25d94ccc40
chore: Update MSRV & add lints rules
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-28 22:27:27 +03:00
e6060d5445
chore(copyright): Update the copyright
Signed-off-by: Awiteb <a@4rs.nl>
2024-05-21 19:31:22 +03:00
18 changed files with 1581 additions and 654 deletions

View file

@ -0,0 +1,31 @@
---
name: Bug
about: Create a bug report to help us improve Telepingbot
title: '...'
labels: ["Kind/Bug"]
assignees: ''
---
## Checks
* [ ] I added a descriptive title to this issue
* [ ] I have searched Google for similar issues and couldn't find anything
* [ ] I have read [the README](https://git.4rs.nl/awiteb/telepingbot/src/branch/master/README.md) and still think this is a bug
## Version
<!-- Report for the bug only if it's present in the latest version of Telepingbot.
If you are not using the latest version, please update and check if the bug is still present. -->
<!-- Run `rustc --version` to get the version -->
Rustc version: `...`
<!-- Run `telepingbot --version` to get the version, and make sure it's the latest one -->
Telepingbot version: `...`
## Description
<!-- A clear and concise description of what the bug is. -->
## Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
## Actual behavior
<!-- A clear and concise description of what happens. -->

View file

@ -0,0 +1,13 @@
---
name: Feature request
about: Suggest an idea for Telepingbot
title: '...'
labels: ["Kind/Feature"]
assignees: ''
---
## Feature description
<!-- A clear and concise description of what the feature is, and why you think it is needed. -->
## Example
<!-- A clear and concise example of how the feature will be used. (If needed) -->

View file

@ -0,0 +1,12 @@
---
name: Question
about: Ask a question about Telepingbot
title: '...'
labels: ["Kind/Question"]
assignees: ''
---
## Question
<!-- Please provide a clear and concise description of your question. -->
<!-- If you are asking about a specific part of the code, please provide a link to the code. -->
<!-- If you are asking about a specific part of the documentation, please provide a link to the documentation. -->

View file

@ -0,0 +1,6 @@
## Issue
<!-- Copy the issue link from the issue you are fixing (e.g. https://git.4rs.nl/awiteb/telepingbot/issues/1) -->
This will fix {issue Forgejo link}
## How I am fixing it
<!-- A clear and concise description of how you are fixing the bug. -->

112
.forgejo/workflows/cd.yml Normal file
View file

@ -0,0 +1,112 @@
name: CD
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+
jobs:
build-assets:
runs-on: debian
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
- x86_64-pc-windows-gnu
steps:
- uses: actions/checkout@v4
with:
ref: master
fetch-depth: 1
- uses: https://codeberg.org/TheAwiteb/rust-action@v1.75
- name: Install musl-tools
run: |
apt-get update
apt-get install -y musl-tools
if: ${{ contains(matrix.target, 'musl') }}
- name: Install gcc-aarch64-linux-gnu linker
run: |
apt-get update
apt-get install -y gcc-aarch64-linux-gnu
if: ${{ contains(matrix.target, 'aarch64') }}
- name: Install gcc-mingw-w64 linker
run: |
apt-get update
apt-get install -y gcc-mingw-w64
if: ${{ contains(matrix.target, 'windows') }}
- name: Preparing the environment
run: |
BIN_NAME="$(echo $GITHUB_REPOSITORY | cut -d '/' -f 2)"
echo "BIN_NAME=$BIN_NAME" >> $GITHUB_ENV
mkdir -p release-dir
mkdir -p .cargo
echo 'target.aarch64-unknown-linux-gnu.linker = "aarch64-linux-gnu-gcc"' > .cargo/config.toml
echo 'target.aarch64-unknown-linux-musl.linker = "aarch64-linux-gnu-gcc"' >> .cargo/config.toml
- name: Install the target
run: rustup target install ${{ matrix.target }}
- name: Build the asset
run: |
APP_NAME="$BIN_NAME-$GITHUB_REF_NAME-${{ matrix.target }}"
cargo clean
cargo build --release --no-default-features --target ${{ matrix.target }}
cp target/${{ matrix.target }}/release/$BIN_NAME.exe release-dir/$APP_NAME.exe || true
cp target/${{ matrix.target }}/release/$BIN_NAME release-dir/$APP_NAME || true
cd release-dir
test -f $APP_NAME && sha256sum $APP_NAME > $APP_NAME.sha256 || true
test -f $APP_NAME.exe && sha256sum $APP_NAME.exe > $APP_NAME.exe.sha256 || true
- uses: actions/upload-artifact@v3
with:
name: ${{ env.GITHUB_SHA }}-${{ env.GITHUB_RUN_NUMBER }}
path: release-dir/
release:
needs: build-assets
runs-on: debian
steps:
- uses: actions/checkout@v4
with:
ref: master
fetch-depth: 0
fetch-tags: true
- uses: actions/download-artifact@v3
with:
name: ${{ env.GITHUB_SHA }}-${{ env.GITHUB_RUN_NUMBER }}
path: ${{ env.GITHUB_WORKSPACE }}/release-dir
- name: Install git-cliff
run: |
version="2.2.1"
wget "https://github.com/orhun/git-cliff/releases/download/v$version/git-cliff-$version-x86_64-unknown-linux-gnu.tar.gz"
tar -xvzf git-cliff-*.tar.gz
mv "git-cliff-$version/git-cliff" /usr/local/bin
rm -fr git-cliff-*
- name: Write changelog
run: |
git config user.name forgejo-actions
git config user.email forgejo-actions@noreply.localhost
echo 'TAG_CHANGELOG=$(if [[ $(git tag --sort=committerdate | tail -n 1) == *"-rc"* ]]; then git-cliff --strip all $(git tag --sort=committerdate | tail -n 2 | sed ":a; N; $!ba; s/\n/../g") | sed "s/## unreleased.*$//g"; else git-cliff -l --strip all | sed "s/^## \[.*$//g";fi)' | sed "s/\"/'/g" >> $GITHUB_ENV
if [[ $(git tag --sort=creatordate | tail -n 1) != *'-rc'* ]]; then
echo "The latest tag is not a release candidate, updating changelog for $GITHUB_REF_NAME"
git-cliff > CHANGELOG.md
git add CHANGELOG.md
git commit -m "Update changelog for $GITHUB_REF_NAME"
git push
echo "Changelog updated"
else
echo "The latest tag is a release candidate, not updating changelog"
fi
- name: Create Release
uses: actions/forgejo-release@v1
with:
direction: upload
url: https://git.4rs.nl
token: ${{ env.GITHUB_TOKEN }}
release-dir: release-dir
release-notes: ${{ env.TAG_CHANGELOG }}
prerelease: ${{ contains(env.GITHUB_REF_NAME, '-rc') }}

View file

@ -0,0 +1,36 @@
name: Write changelog
on:
push:
branches:
- 'master'
jobs:
write-changelog:
runs-on: debian
steps:
- uses: actions/checkout@v4
with:
ref: master
fetch-depth: 0
fetch-tags: true
- name: Install git-cliff
run: |
version="2.2.1"
wget "https://github.com/orhun/git-cliff/releases/download/v$version/git-cliff-$version-x86_64-unknown-linux-gnu.tar.gz"
tar -xvzf git-cliff-*.tar.gz
mv "git-cliff-$version/git-cliff" /usr/local/bin
rm -fr git-cliff-*
- name: Write changelog
run: |
git config user.name forgejo-actions
git config user.email forgejo-actions@noreply.localhost
git-cliff > CHANGELOG.md
if [[ $(git status | grep --extended-regexp '^\s+modified:\s+CHANGELOG.md$') ]]; then
git add CHANGELOG.md
git commit -m "chore(changelog): Update changelog"
git push
echo "Changelog updated"
else
echo "No changes to changelog"
fi

25
.forgejo/workflows/ci.yml Normal file
View file

@ -0,0 +1,25 @@
name: Rust CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
rust_ci:
name: Rust CI
runs-on: debian
steps:
- uses: actions/checkout@v4
- uses: https://codeberg.org/TheAwiteb/rust-action@v1.75
- name: Check MSRV
run: cargo +1.75 build
- name: Build the source code
run: cargo build
- name: Check the code format
run: cargo fmt -- --check
- name: Run cargo-check
run: cargo check
- name: Run cargo-clippy
run: cargo clippy -- -D warnings

34
.github/workflows/auto_close_pr.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Auto close PR
on:
pull_request:
types: [opened, reopened]
jobs:
close_pr:
name: Auto close PR
runs-on: ubuntu-latest
steps:
- name: Send close comment
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $PAT" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.number }}/comments \
-d '{"body":"${{ env.BODY }}"}'
env:
PAT: ${{ secrets.PAT }}
BODY: This repository is mirror only and you cannot create a pull request for it. Please open your PR at https://git.4rs.nl/awiteb/telepingbot
- name: Close the PR
run: |
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $PAT" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }} \
-d '{"state":"closed"}'
env:
PAT: ${{ secrets.PAT }}

11
CHANGELOG.md Normal file
View file

@ -0,0 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## unreleased
## 0.1.0 - 2023-11-18
This changelog was generated by [git-cliff](https://github.com/orhun/git-cliff)

1601
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,27 +1,80 @@
[package] [package]
authors = ["TheAwiteb <awiteb@hotmail.com>"] authors = ["Awiteb <a@4rs.nl>"]
description = "Simple API to ping a telegram bot using superbot (mtproto)" description = "Simple API to ping a telegram bot using superbot (mtproto)"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
name = "telepingbot" name = "telepingbot"
readme = "README.md" readme = "README.md"
repository = "https://github.com/TheAwiteb/telepingbot" repository = "https://git.4rs.nl/awiteb/telepingbot"
rust-version = "1.68.2" rust-version = "1.75.0"
version = "0.1.0" version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
chrono = "0.4.31" chrono = "0.4.38"
dotenv = "0.15.0" dotenv = "0.15.0"
grammers-client = "= 0.4.0" grammers-client = "0.6.0"
grammers-session = "= 0.4.0" grammers-session = "0.5.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.20" log = "0.4.21"
pretty_env_logger = "0.5.0" pretty_env_logger = "0.5.0"
promptly = "0.3.1" promptly = "0.3.1"
salvo = {version = "0.58.3", features = ["logging", "affix"]} salvo = {version = "0.67.2", features = ["logging", "affix", "rustls"]}
serde = {version = "1.0.192", features = ["derive"]} serde = {version = "1.0.202", features = ["derive"]}
serde_json = "1.0.108" serde_json = "1.0.117"
sha256 = "1.4.0" sha256 = "1.5.0"
tokio = {version = "1.34.0", features = ["macros", "rt-multi-thread", "signal"]} tokio = {version = "1.37.0", features = ["macros", "rt-multi-thread", "signal"]}
[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
[lints.clippy]
# I know is huge, but I like to be explicit, it also provides
# a better DX for new contributors (Make it easier to understand the codebase).
# Also, this is a general linting configuration, it's not specific to this project.
wildcard_imports = "deny"
manual_let_else = "deny"
match_bool = "deny"
match_on_vec_items = "deny"
or_fun_call = "deny"
panic = "deny"
unwrap_used = "deny"
missing_assert_message = "warn"
missing_const_for_fn = "warn"
missing_errors_doc = "warn"
absolute_paths = "warn"
cast_lossless = "warn"
clone_on_ref_ptr = "warn"
cloned_instead_of_copied = "warn"
dbg_macro = "warn"
default_trait_access = "warn"
empty_enum_variants_with_brackets = "warn"
empty_line_after_doc_comments = "warn"
empty_line_after_outer_attr = "warn"
empty_structs_with_brackets = "warn"
enum_glob_use = "warn"
equatable_if_let = "warn"
explicit_iter_loop = "warn"
filetype_is_file = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
float_cmp = "warn"
format_push_string = "warn"
future_not_send = "warn"
if_not_else = "warn"
if_then_some_else_none = "warn"
implicit_clone = "warn"
inconsistent_struct_constructor = "warn"
indexing_slicing = "warn"
iter_filter_is_ok = "warn"
iter_filter_is_some = "warn"
iter_not_returning_iterator = "warn"
manual_is_variant_and = "warn"
option_if_let_else = "warn"
option_option = "warn"
[profile.release]
strip = true # Automatically strip symbols from the binary.

30
Justfile Normal file
View file

@ -0,0 +1,30 @@
# This justfile is for the contrbutors of this project, not for the end user.
#
# Requirements for this justfile:
# - Linux distribution
# - just (Of course) <https://github.com/casey/just>
# - cargo (For the build and tests) <https://doc.rust-lang.org/cargo/getting-started/installation.html>
set shell := ["/usr/bin/bash", "-c"]
JUST_EXECUTABLE := "just -u -f " + justfile()
header := "Available tasks:\n"
# Get the MSRV from the Cargo.toml
msrv := `cat Cargo.toml | grep "rust-version" | sed 's/.*"\(.*\)".*/\1/'`
_default:
@{{JUST_EXECUTABLE}} --list-heading "{{header}}" --list
# Run the CI
@ci: && msrv
cargo build -q
cargo fmt -- --check
cargo clippy -- -D warnings
# Check that the current MSRV is correct
@msrv:
rustup toolchain install {{msrv}}
echo "Checking MSRV ({{msrv}})"
cargo +{{msrv}} check -q
echo "MSRV is correct"

112
cliff.toml Normal file
View file

@ -0,0 +1,112 @@
[changelog]
# changelog header
header = """
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n
"""
body = """
{# Check if there is a `BREAKING-CHANGE` footer in the message or not #}\
{% macro is_breaking(commit) %}\
{{"BREAKING-CHANGE" in commit.message}}\
{% endmacro is_breaking %}\
{# Return the commit header #}\
{% macro commit_header(commit) %}\
{{commit.message | split(pat="\n") | nth(n=0) | upper_first }}\
{% endmacro commit_header %}\
{# Return the `BREAKING-CHANGE` footer message #}\
{% macro bc_des(commit) %}\
{% set lines = [] %}\
{% set found_breaking = false %}\
{% for line in commit.message | split(pat="\n") %}\
{% if found_breaking and line is not containing(":") %}\
{% set_global lines = lines | concat(with=line) %}\
{% elif found_breaking and line is containing(":") %}\
{% set_global found_breaking = false %}\
{% endif %}\
{% if line is starting_with("BREAKING-CHANGE") %}\
{% set_global found_breaking = true %}\
{% set breaking_line = line | split(pat=":") | nth(n=1) %}\
{% set_global lines = lines | concat(with=breaking_line) %}\
{% endif %}\
{% endfor %}\
{% if lines | length != 0 %}\
{{ lines | join(sep="\n") | trim_end(pat="\n") }}\
{% endif %}\
{% endmacro bc_des %}\
{% if version %}\
{% if previous.version %}\
## [{{ version | trim_start_matches(pat="v") }}](<REPO>/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif %}\
{% else %}\
## unreleased
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}\
### {{ group | upper_first }}
{% for commit in commits | sort(attribute="message") %}\
- {{ self::commit_header(commit=commit) }} ([`{{ commit.id | truncate(length=7, end="") }}`](<REPO>/commit/{{ commit.id }}))
{% if self::is_breaking(commit=commit) == "true" %}\
\t- **BC**: {{self::bc_des(commit=commit)}}
{% endif %}\
{% endfor %}\
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
This changelog was generated by [git-cliff](https://github.com/orhun/git-cliff)
"""
# postprocessors
postprocessors = [
{pattern = '<REPO>', replace = "https://git.4rs.nl/awiteb/telepingbot"}, # replace repository URL
{pattern = '- (\w+)(\(\w+\))?:', replace = "- "}, # Remove the type
{pattern = '- \((\w+)\):', replace = "- (**$1**)"}, # Make the scope blod
{pattern = "\t", replace = " "}, # Replace tap with 4 spaces
]
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = false
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
{pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([**#${2}**](<REPO>/issues/${2}))"}, # replace issue numbers (Note the PR is also an issue in Forgejo, so this will also link to PRs)
{pattern = ' +$', replace = ""}, # Remove trailing whitespace.
{pattern = ' +', replace = " "}, # Replace multiple spaces with a single space.
]
# regex for parsing and grouping commits
commit_parsers = [
{message = "^feat", group = "Added"},
{message = "^fix", group = "Fixed"},
{message = "^(refactor|change)", group = "Changed"},
{message = "^deprecate", group = "Deprecated"},
{message = "^remove", group = "Removed"},
{message = "^security", group = "Security"},
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# regex for matching git tags
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
# regex for skipping tags
skip_tags = ""
# regex for ignoring tags
ignore_tags = "v[0-9]+\\.[0-9]+\\.[0-9]+-(alpha|beta|rc)(\\.[0-9]+)?"
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"

13
rust-toolchain.toml Normal file
View file

@ -0,0 +1,13 @@
[toolchain]
# We use nightly in development only, the project will always be compliant with
# the latest stable release and the MSRV as defined in `Cargo.toml` file.
channel = "nightly-2024-05-25"
components = [
"rustc",
"cargo",
"rust-std",
"rust-src",
"rustfmt",
"rust-analyzer",
"clippy",
]

22
rustfmt.toml Normal file
View file

@ -0,0 +1,22 @@
unstable_features = true
version = "Two"
blank_lines_upper_bound = 2
combine_control_expr = false
wrap_comments = true
condense_wildcard_suffixes = true
edition = "2021"
enum_discrim_align_threshold = 20
force_multiline_blocks = true
format_code_in_doc_comments = true
format_generated_files = false
format_macro_matchers = true
format_strings = true
imports_layout = "HorizontalVertical"
newline_style = "Unix"
normalize_comments = true
reorder_impl_items = true
group_imports = "StdExternalCrate"
single_line_let_else_max_width = 0
struct_field_align_threshold = 20
use_try_shorthand = true

View file

@ -1,5 +1,5 @@
// A simple API to ping telegram bots and returns if it's online or not. // A simple API to ping telegram bots and returns if it's online or not.
// Copyright (C) 2023 Awiteb <awitb@hotmail.com> // Copyright (C) 2023-2024 Awiteb <a@4rs.nl>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published // it under the terms of the GNU Affero General Public License as published
@ -12,13 +12,13 @@
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0>.
use std::sync::Arc; use std::sync::Arc;
use salvo::{catcher::Catcher, http::HeaderValue, hyper::header, logging::Logger, prelude::*}; use salvo::{catcher::Catcher, http::HeaderValue, hyper::header, logging::Logger, prelude::*};
use crate::PingList; use crate::{superbot, PingList};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct AppState { pub(crate) struct AppState {
@ -61,7 +61,7 @@ impl AppState {
impl<'a> MessageSchema<'a> { impl<'a> MessageSchema<'a> {
/// Create new [`Message`] instance with `200 OK` status /// Create new [`Message`] instance with `200 OK` status
fn new(message: &'a str) -> Self { const fn new(message: &'a str) -> Self {
Self { Self {
message, message,
status: true, status: true,
@ -78,20 +78,23 @@ impl<'a> MessageSchema<'a> {
} }
fn write_json_body(res: &mut Response, json_body: impl serde::Serialize) { fn write_json_body(res: &mut Response, json_body: impl serde::Serialize) {
res.write_body(serde_json::to_string(&json_body).unwrap()) res.write_body(serde_json::to_string(&json_body).expect("This should not fail"))
.ok(); .ok();
} }
#[handler] #[handler]
async fn ping(req: &Request, res: &mut Response, depot: &mut Depot) { async fn ping(req: &Request, res: &mut Response, depot: &mut Depot) {
let bot_username = req.param::<String>("bot_username").unwrap().to_lowercase(); let bot_username = req
let app_state = depot.obtain::<Arc<AppState>>().unwrap(); .param::<String>("bot_username")
.expect("The path param is required");
let app_state = depot
.obtain::<Arc<AppState>>()
.expect("The app state is injected");
let msg = if !app_state.bots.contains(&bot_username) { let msg = if !app_state.bots.contains(&bot_username) {
MessageSchema::new("Is not authorized to check the status of this bot") MessageSchema::new("Is not authorized to check the status of this bot")
.code(StatusCode::BAD_REQUEST) .code(StatusCode::BAD_REQUEST)
} else if let Ok(telegram_id) = } else if let Ok(telegram_id) = superbot::send_start(&app_state.tg_client, &bot_username).await
crate::superbot::send_start(&app_state.tg_client, &bot_username).await
{ {
if crate::PINGED_BOTS.check(telegram_id) { if crate::PINGED_BOTS.check(telegram_id) {
MessageSchema::new("Alive") MessageSchema::new("Alive")
@ -107,7 +110,7 @@ async fn ping(req: &Request, res: &mut Response, depot: &mut Depot) {
#[handler] #[handler]
async fn handle404(res: &mut Response, ctrl: &mut FlowCtrl) { async fn handle404(res: &mut Response, ctrl: &mut FlowCtrl) {
if let Some(StatusCode::NOT_FOUND) = res.status_code { if res.status_code == Some(StatusCode::NOT_FOUND) {
write_json_body( write_json_body(
res, res,
MessageSchema::new("Not Found").code(StatusCode::NOT_FOUND), MessageSchema::new("Not Found").code(StatusCode::NOT_FOUND),
@ -129,7 +132,9 @@ async fn handle_server_errors(res: &mut Response, ctrl: &mut FlowCtrl) {
#[handler] #[handler]
async fn auth(req: &Request, res: &mut Response, depot: &mut Depot, ctrl: &mut FlowCtrl) { async fn auth(req: &Request, res: &mut Response, depot: &mut Depot, ctrl: &mut FlowCtrl) {
let app_state = depot.obtain::<Arc<AppState>>().unwrap(); let app_state = depot
.obtain::<Arc<AppState>>()
.expect("The app state is injected");
log::info!("New auth request"); log::info!("New auth request");
if let Some(token) = req.headers().get("Authorization") { if let Some(token) = req.headers().get("Authorization") {
if let Ok(token) = token.to_str() { if let Ok(token) = token.to_str() {

View file

@ -1,5 +1,5 @@
// A simple API to ping telegram bots and returns if it's online or not. // A simple API to ping telegram bots and returns if it's online or not.
// Copyright (C) 2023 Awiteb <awitb@hotmail.com> // Copyright (C) 2023-2024 Awiteb <a@4rs.nl>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published // it under the terms of the GNU Affero General Public License as published
@ -12,12 +12,14 @@
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0>.
#![doc = include_str!("../README.md")]
use std::{env, fs, sync::Mutex}; use std::{env, fs, sync::Mutex};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use salvo::Listener; use salvo::{conn::TcpListener, Listener};
mod api; mod api;
mod superbot; mod superbot;
@ -40,7 +42,9 @@ impl PingList for Mutex<Vec<PingedBot>> {
fn clear_outdead(&self) { fn clear_outdead(&self) {
log::info!("Clear the dead pings"); log::info!("Clear the dead pings");
let dead_time = chrono::Utc::now().timestamp() - 60; let dead_time = chrono::Utc::now().timestamp() - 60;
let mut bots = self.lock().unwrap(); let mut bots = self
.lock()
.expect("Another holder paniced while holding the lock");
*bots = bots *bots = bots
.iter() .iter()
.filter(|b| b.ping_in > dead_time) .filter(|b| b.ping_in > dead_time)
@ -50,7 +54,9 @@ impl PingList for Mutex<Vec<PingedBot>> {
fn add_new(&self, telegram_id: u64) { fn add_new(&self, telegram_id: u64) {
log::debug!("Adding new bot to the list: {telegram_id}"); log::debug!("Adding new bot to the list: {telegram_id}");
self.lock().unwrap().push(PingedBot::new(telegram_id)); self.lock()
.expect("Another holder paniced while holding the lock")
.push(PingedBot::new(telegram_id));
} }
fn check(&self, telegram_id: u64) -> bool { fn check(&self, telegram_id: u64) -> bool {
@ -58,15 +64,18 @@ impl PingList for Mutex<Vec<PingedBot>> {
self.clear_outdead(); self.clear_outdead();
let result = self let result = self
.lock() .lock()
.unwrap() .expect("Another holder paniced while holding the lock")
.iter() .iter()
.any(|b| b.telegram_id == telegram_id && b.is_response); .any(|b| b.telegram_id == telegram_id && b.is_response);
log::debug!("Response status: {result}"); log::debug!("Response status: {result}");
result result
} }
fn new_res(&self, telegram_id: u64) { fn new_res(&self, telegram_id: u64) {
log::debug!("New res from: {telegram_id}"); log::debug!("New res from: {telegram_id}");
let mut bots = self.lock().unwrap(); let mut bots = self
.lock()
.expect("Another holder paniced while holding the lock");
*bots = bots *bots = bots
.iter() .iter()
.cloned() .cloned()
@ -91,12 +100,13 @@ impl PingedBot {
} }
} }
pub(crate) fn new_res(mut self) -> Self { pub(crate) const fn new_res(mut self) -> Self {
self.is_response = true; self.is_response = true;
self self
} }
} }
#[allow(clippy::absolute_paths)]
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
lazy_static! { lazy_static! {
@ -146,21 +156,11 @@ async fn main() -> Result<()> {
let app_state = api::AppState::new(bots, tokens, client.clone()); let app_state = api::AppState::new(bots, tokens, client.clone());
let handler_client = client.clone(); let handler_client = client.clone();
let acceptor = salvo::conn::TcpListener::new(format!("{host}:{port}")) let acceptor = TcpListener::new(format!("{host}:{port}")).bind().await;
.bind()
.await;
let client_handler = tokio::spawn(async move { superbot::handler(handler_client).await }); let client_handler = tokio::spawn(async move { superbot::handler(handler_client).await });
let server_handler = tokio::spawn(async move { let server_handler = tokio::spawn(async move {
salvo::Server::new(acceptor) salvo::Server::new(acceptor)
.serve_with_graceful_shutdown( .serve(api::service(app_state))
api::service(app_state),
async {
tokio::signal::ctrl_c()
.await
.expect("Faild to listen to ctrl_c event");
},
None,
)
.await .await
}); });

View file

@ -1,5 +1,5 @@
// A simple API to ping telegram bots and returns if it's online or not. // A simple API to ping telegram bots and returns if it's online or not.
// Copyright (C) 2023 Awiteb <awitb@hotmail.com> // Copyright (C) 2023-2024 Awiteb <a@4rs.nl>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published // it under the terms of the GNU Affero General Public License as published
@ -12,10 +12,11 @@
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0>.
use grammers_client::{Client, Config, InitParams, SignInError, Update}; use grammers_client::{Client, Config, InitParams, SignInError, Update};
use grammers_session::Session; use grammers_session::Session;
use tokio::{signal, time};
use crate::PingList; use crate::PingList;
@ -34,7 +35,7 @@ pub(crate) async fn login(api_hash: String, api_id: i32) -> crate::Result<(Clien
if !client.is_authorized().await? { if !client.is_authorized().await? {
println!("Signing in..."); println!("Signing in...");
let phone: String = promptly::prompt("Enter your phone number (international format)")?; let phone: String = promptly::prompt("Enter your phone number (international format)")?;
let token = client.request_login_code(&phone, api_id, &api_hash).await?; let token = client.request_login_code(&phone).await?;
let code: String = promptly::prompt("Enter the code you received")?; let code: String = promptly::prompt("Enter the code you received")?;
let signed_in = client.sign_in(&token, &code).await; let signed_in = client.sign_in(&token, &code).await;
match signed_in { match signed_in {
@ -47,7 +48,7 @@ pub(crate) async fn login(api_hash: String, api_id: i32) -> crate::Result<(Clien
.await?; .await?;
} }
Ok(_) => (), Ok(_) => (),
Err(e) => panic!("{e}"), Err(e) => return Err(e.into()),
} }
let me = client.get_me().await?; let me = client.get_me().await?;
println!( println!(
@ -82,7 +83,7 @@ fn update_handler(upd: Update) {
pub(crate) async fn handler(client: Client) { pub(crate) async fn handler(client: Client) {
loop { loop {
tokio::select! { tokio::select! {
_ = tokio::signal::ctrl_c() => { _ = signal::ctrl_c() => {
break; break;
} }
Ok(Some(update)) = client.next_update() => { Ok(Some(update)) = client.next_update() => {
@ -101,7 +102,7 @@ pub(crate) async fn send_start(client: &Client, bot_username: &str) -> crate::Re
crate::PINGED_BOTS.add_new(telegram_id); crate::PINGED_BOTS.add_new(telegram_id);
client.send_message(chat, "/start").await?; client.send_message(chat, "/start").await?;
// Sleep, wating the response // Sleep, wating the response
tokio::time::sleep(std::time::Duration::from_secs(2)).await; time::sleep(time::Duration::from_secs(2)).await;
Ok(telegram_id) Ok(telegram_id)
} else { } else {
Err(format!("Invalid username `{bot_username}`").into()) Err(format!("Invalid username `{bot_username}`").into())