Compare commits
No commits in common. "alpha" and "0.1.0" have entirely different histories.
|
@ -1,8 +0,0 @@
|
||||||
FROM archlinux:base-devel
|
|
||||||
RUN pacman -Syuu --noconfirm
|
|
||||||
WORKDIR /opt/aur_builder
|
|
||||||
RUN useradd -m -g users -G wheel -s /bin/bash aur && echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
||||||
ADD https://git.orudo.ru/OrudoCA/aur_builder_bot/releases/download/0.1.0/aur_builder_bot /opt/aur_builder/aur_builder_bot
|
|
||||||
RUN chmod +x aur_builder_bot && ln -sf /opt/aur_builder/aur_builder_bot /usr/bin/aur_builder_bot && chown aur -R /opt/aur_builder
|
|
||||||
USER aur
|
|
||||||
ENTRYPOINT ["aur_builder_bot"]
|
|
51
README.md
51
README.md
|
@ -13,57 +13,34 @@
|
||||||
|
|
||||||
1. Укажите токен бота телеграм
|
1. Укажите токен бота телеграм
|
||||||
```bash
|
```bash
|
||||||
$ export TELOXIDE_TOKEN=<Ваш токен>
|
# Unix-like
|
||||||
|
$ export TELOXIDE_TOKEN=<Your token here>
|
||||||
|
|
||||||
|
# Windows command line
|
||||||
|
$ set TELOXIDE_TOKEN=<Your token here>
|
||||||
|
|
||||||
|
# Windows PowerShell
|
||||||
|
$ $env:TELOXIDE_TOKEN=<Your token here>
|
||||||
```
|
```
|
||||||
2. Укажите пароль для авторизации
|
|
||||||
```bash
|
3. Запустите бота
|
||||||
$ export PASS=<Пароль>
|
|
||||||
```
|
|
||||||
3. Добаьте GPG ключ для подписи (опционально)
|
|
||||||
- ```$ gpg --list-keys --keyid-format=long```
|
|
||||||
- ```rsa4096/D8DDA4AE70FAD33E``` копируем ID ключа (здесь **D8DDA4AE70FAD33E**)
|
|
||||||
- ```$ export GPGKEY=<ID ключа>```
|
|
||||||
4. Запустите бота
|
|
||||||
```bash
|
```bash
|
||||||
./aur_builder_bot
|
./aur_builder_bot
|
||||||
```
|
```
|
||||||
5. Создайте симлинк до вашего локального репозитория
|
4. Создайте симлинк до вашего локального репозитория
|
||||||
```bash
|
```bash
|
||||||
ln -s /path/to/bot_dir/repo /path/to/repo
|
ln -s /path/to/bot/repo /path/to/repo
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
**docker-cli**
|
В разработке
|
||||||
```bash
|
|
||||||
docker run \
|
|
||||||
--name AUR_Builder_Bot \
|
|
||||||
--restart=unless-stopped \
|
|
||||||
-v /path/to/repo:/opt/aur_build/repo \
|
|
||||||
-e TELOXIDE_TOKEN="<Your_token_here>"
|
|
||||||
-d orudoca/aur_builder_bot:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
**docker-compose**
|
|
||||||
```yml
|
|
||||||
services:
|
|
||||||
aur_builder_bot:
|
|
||||||
image: orudoca/aur_builder_bot:latest
|
|
||||||
container_name: AUR_Builder_Bot
|
|
||||||
volumes:
|
|
||||||
- /path/to/repo/:/opt/aur_builder/repo
|
|
||||||
restart: 'unless-stopped'
|
|
||||||
environment:
|
|
||||||
TELOXIDE_TOKEN: "<Your_token_here>"
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
## Использоание
|
## Использоание
|
||||||
|
|
||||||
1. Поиск пакетов в AUR
|
1. Поиск пакетов в AUR
|
||||||
`/search <название пакета> <кол-во отображаемых пакетов 1-255>`
|
`/search <название пакета>`
|
||||||
|
|
||||||
2. Добавление пакетов в репозиторий
|
2. Добавление пакетов в репозиторий
|
||||||
`/upload <название пакета>`
|
`/upload <название пакета>`
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
services:
|
|
||||||
aur_builder_bot:
|
|
||||||
image: orudoca/aur_builder_bot:latest
|
|
||||||
container_name: AUR_Builder_Bot
|
|
||||||
volumes:
|
|
||||||
- /path/to/repo/:/opt/aur_builder/repo
|
|
||||||
restart: 'unless-stopped'
|
|
||||||
environment:
|
|
||||||
TELOXIDE_TOKEN: "<Your_token_here>"
|
|
|
@ -1,69 +0,0 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
fn write(data: Vec<i64>) -> Result<(), Box<dyn std::error::Error>>{
|
|
||||||
match File::create("auth") {
|
|
||||||
Ok(mut file) => {
|
|
||||||
for num in data {
|
|
||||||
file.write_all(format!("{}\n", num).as_bytes())?; }
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read() -> Result<Vec<i64>, Box<dyn std::error::Error>>{
|
|
||||||
let mut file = File::open("auth")?;
|
|
||||||
let mut content = String::new();
|
|
||||||
file.read_to_string(&mut content)?;
|
|
||||||
let ids: Vec<i64> = content
|
|
||||||
.lines()
|
|
||||||
.filter_map(|line| line.parse().ok())
|
|
||||||
.collect();
|
|
||||||
Ok(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(id: i64) -> Result<(), Box<dyn std::error::Error>>{
|
|
||||||
if check(id) {} else {
|
|
||||||
match read() {
|
|
||||||
Ok(ids) => {
|
|
||||||
let mut array: Vec<i64> = ids;
|
|
||||||
array.push(id);
|
|
||||||
write(array)?;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error reading from file: {}, recreating auth", e);
|
|
||||||
init(id)?;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check(id: i64) -> bool {
|
|
||||||
let mut valid: Option<bool> = None;
|
|
||||||
match read() {
|
|
||||||
Ok(ids) => {
|
|
||||||
let array: &[i64] = ids.as_slice();
|
|
||||||
if array.contains(&id) {
|
|
||||||
valid = Some(true);
|
|
||||||
} else {
|
|
||||||
valid = Some(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error reading from file: {}, recreating auth", e);
|
|
||||||
init(id).expect("init");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return valid.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn init(id: i64) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mut file = File::create("auth")?;
|
|
||||||
let vec: Option<Vec<i64>> = Some(Vec::from([id]));
|
|
||||||
for num in vec.unwrap() {
|
|
||||||
file.write_all(format!("{}\n", num).as_bytes())?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
47
src/build.rs
47
src/build.rs
|
@ -1,63 +1,43 @@
|
||||||
use git2::{Error, Repository};
|
use git2::{Error, Repository};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use glob::glob;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn clone(pkg: String, dir: String) -> Result<Repository, Error> {
|
pub fn clone(pkg: String, dir: String) -> Result<Repository, Error> {
|
||||||
let repo = format!("https://aur.archlinux.org/{}.git", pkg);
|
let repo = format!("https://aur.archlinux.org/{}.git", pkg);
|
||||||
Repository::clone(repo.as_str(), dir.as_str())
|
Repository::clone(repo.as_str(), dir.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy(dir: String, local_repo: String) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn copy(dir: String, local_repo: String) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let entries = fs::read_dir(dir)?;
|
let entries = fs::read_dir(dir)?;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let file_path = entry.path();
|
let file_path = entry.path();
|
||||||
match std::env::var("GPGKEY") {
|
|
||||||
Ok(_) => {
|
if file_path.is_file() && file_path.extension().unwrap_or_default() == "zst" {
|
||||||
zst(file_path.clone(), local_repo.clone())?;
|
|
||||||
if file_path.is_file() && file_path.extension().unwrap_or_default() == "sig" {
|
|
||||||
let file_name = file_path.file_name().unwrap();
|
let file_name = file_path.file_name().unwrap();
|
||||||
let local_repo = Path::new(local_repo.as_str()).join(file_name);
|
let local_repo = Path::new(local_repo.as_str()).join(file_name);
|
||||||
fs::copy(&file_path, &local_repo)?;
|
fs::copy(&file_path, &local_repo)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
|
||||||
zst(file_path, local_repo.clone())?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn build() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match std::env::var("GPGKEY") {
|
|
||||||
Ok(value) => {
|
|
||||||
Command::new("makepkg")
|
|
||||||
.arg("-s")
|
|
||||||
.arg("--sign")
|
|
||||||
.arg(format!("--key={}", value))
|
|
||||||
.arg("--noconfirm")
|
|
||||||
.output()?;
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
Command::new("makepkg")
|
Command::new("makepkg")
|
||||||
.arg("-s")
|
.arg("-s")
|
||||||
.arg("--noconfirm")
|
.arg("--noconfirm")
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn delete(dir: String) -> std::io::Result<()> {
|
pub fn delete(dir: String) -> std::io::Result<()> {
|
||||||
fs::remove_dir_all(dir)
|
fs::remove_dir_all(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_add() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn repo_add() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let files: Vec<String> = glob::glob("*.pkg.tar.zst")
|
let files: Vec<String> = glob("*.pkg.tar.zst")
|
||||||
.expect("Failed to read glob pattern")
|
.expect("Failed to read glob pattern")
|
||||||
.filter_map(|entry| entry.ok())
|
.filter_map(|entry| entry.ok())
|
||||||
.filter_map(|path| path.to_str().map(String::from))
|
.filter_map(|path| path.to_str().map(String::from))
|
||||||
|
@ -74,12 +54,3 @@ pub fn repo_add() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.output()?;
|
.output()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zst(file_path: PathBuf, local_repo: String) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
if file_path.is_file() && file_path.extension().unwrap_or_default() == "zst" {
|
|
||||||
let file_name = file_path.file_name().unwrap();
|
|
||||||
let local_repo = Path::new(local_repo.as_str()).join(file_name);
|
|
||||||
fs::copy(&file_path, &local_repo)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
mod telegram;
|
mod telegram;
|
||||||
mod build;
|
mod build;
|
||||||
mod search;
|
mod search;
|
||||||
mod authorization;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use aur_rpc;
|
use aur_rpc;
|
||||||
pub async fn search(pkg: String, num: u8) -> String {
|
pub async fn search(pkg: String) -> String {
|
||||||
let mut packages = aur_rpc::search(pkg).await.unwrap();
|
let mut packages = aur_rpc::search(pkg).await.unwrap();
|
||||||
packages.sort_by(|a, b| b.num_votes.cmp(&a.num_votes));
|
packages.sort_by(|a, b| b.num_votes.cmp(&a.num_votes));
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
for (index, package) in packages.iter().enumerate().take(num as usize) {
|
for (index, package) in packages.iter().enumerate().take(10) {
|
||||||
result.push(format!("{}. {}", index+1, package.name));
|
result.push(format!("{}. {}", index+1, package.name));
|
||||||
}
|
}
|
||||||
let response = result.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("\n");
|
let response = result.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("\n");
|
||||||
|
|
|
@ -2,24 +2,19 @@ use teloxide::{prelude::*, utils::command::BotCommands};
|
||||||
use crate::build::{clone, copy, build, delete, repo_add};
|
use crate::build::{clone, copy, build, delete, repo_add};
|
||||||
use crate::search;
|
use crate::search;
|
||||||
use std::env;
|
use std::env;
|
||||||
use crate::authorization::{add, check};
|
|
||||||
|
|
||||||
pub async fn main(bot: Bot){
|
pub async fn main(bot: Bot){
|
||||||
Commands::repl(bot, answer).await;
|
Commands::repl(bot, answer).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BotCommands, Clone)]
|
#[derive(BotCommands, Clone)]
|
||||||
#[command(rename_rule = "lowercase", description = "Commands:")]
|
#[command(rename_rule = "lowercase", description = "Commands:")]
|
||||||
enum Commands{
|
pub(crate) enum Commands{
|
||||||
#[command(description = "Build package.")]
|
|
||||||
Upload(String),
|
Upload(String),
|
||||||
#[command(description = "Search packages", parse_with = "split")]
|
Search(String)
|
||||||
Search{ pkg: String, num: u8 },
|
|
||||||
Auth(String)
|
|
||||||
}
|
}
|
||||||
async fn answer(bot: Bot, msg: Message, cmd: Commands) -> ResponseResult<()> {
|
async fn answer(bot: Bot, msg: Message, cmd: Commands) -> ResponseResult<()> {
|
||||||
match cmd {
|
match cmd {
|
||||||
Commands::Upload(pkg) => { if check(msg.chat.id.0) {
|
Commands::Upload(pkg) => {
|
||||||
let default_dir = env::current_dir()?;
|
let default_dir = env::current_dir()?;
|
||||||
let pkg_dir = format!("pkgs/{}", pkg);
|
let pkg_dir = format!("pkgs/{}", pkg);
|
||||||
let repo_dir = format!("repo/");
|
let repo_dir = format!("repo/");
|
||||||
|
@ -31,51 +26,36 @@ async fn answer(bot: Bot, msg: Message, cmd: Commands) -> ResponseResult<()> {
|
||||||
bot.send_message(msg.chat.id, clone).await?;
|
bot.send_message(msg.chat.id, clone).await?;
|
||||||
|
|
||||||
env::set_current_dir(pkg_dir.clone())?;
|
env::set_current_dir(pkg_dir.clone())?;
|
||||||
let build = match build() {
|
let build = match build().await {
|
||||||
Ok(..) => { format!("Builded") },
|
Ok(..) => { format!("Builded") },
|
||||||
Err(e) => { format!("Build error: {}", e) }
|
Err(e) => { format!("Build error: {}", e) }
|
||||||
};
|
};
|
||||||
bot.send_message(msg.chat.id, build).await?;
|
bot.send_message(msg.chat.id, build).await?;
|
||||||
|
|
||||||
env::set_current_dir(default_dir.clone())?;
|
env::set_current_dir(default_dir.clone())?;
|
||||||
let copy = match copy(pkg_dir.clone(), repo_dir.clone()) {
|
let copy = match copy(pkg_dir.clone(), repo_dir.clone()).await {
|
||||||
Ok(..) => { format!("Copied") },
|
Ok(..) => { format!("Copied") },
|
||||||
Err(e) => { format!("Copy error: {}", e) }
|
Err(e) => { format!("Copy error: {}", e) }
|
||||||
};
|
};
|
||||||
bot.send_message(msg.chat.id, copy).await?;
|
bot.send_message(msg.chat.id, copy).await?;
|
||||||
|
|
||||||
let delete = match delete(pkg_dir) {
|
let delete = match delete(pkg_dir) {
|
||||||
Ok(..) => { format!("Sources deleted") },
|
Ok(..) => { format!("Sources deleted")},
|
||||||
Err(e) => { format!("Sources deletion error: {}", e) }
|
Err(e) => { format!("Sources deletion error: {}", e) }
|
||||||
};
|
};
|
||||||
bot.send_message(msg.chat.id, delete).await?;
|
bot.send_message(msg.chat.id, delete).await?;
|
||||||
|
|
||||||
env::set_current_dir(repo_dir)?;
|
env::set_current_dir(repo_dir)?;
|
||||||
let repo_add = match repo_add() {
|
let repo_add = match repo_add().await {
|
||||||
Ok(..) => { format!("Added to repo") },
|
Ok(..) => { format!("Added to repo") },
|
||||||
Err(e) => { format!("Error adding package to repository: {}", e) }
|
Err(e) => { format!("Error adding package to repository: {}", e) }
|
||||||
};
|
};
|
||||||
bot.send_message(msg.chat.id, repo_add).await?;
|
bot.send_message(msg.chat.id, repo_add).await?;
|
||||||
env::set_current_dir(default_dir)?;
|
env::set_current_dir(default_dir)?;
|
||||||
} else { bot.send_message(msg.chat.id, "This bot is private, motherfucker.").await?; }
|
|
||||||
}
|
|
||||||
Commands::Search{pkg, num} => {
|
|
||||||
bot.send_message(msg.chat.id, search::search(pkg, num).await).await?;
|
|
||||||
}
|
|
||||||
Commands::Auth(pass) => {
|
|
||||||
match env::var("PASS") {
|
|
||||||
Ok(value) => {
|
|
||||||
if pass == value {
|
|
||||||
bot.send_message(msg.chat.id, format!("Authorized!")).await?;
|
|
||||||
add(msg.chat.id.0).expect("add");
|
|
||||||
} else {
|
|
||||||
bot.send_message(msg.chat.id, format!("This bot is private, motherfucker.")).await?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
bot.send_message(msg.chat.id, format!("The password variable is not set.")).await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Commands::Search(pkg) => {
|
||||||
|
bot.send_message(msg.chat.id, search::search(pkg).await).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue