Compare commits

..

25 Commits

Author SHA1 Message Date
1e2114aaa3 packages.biu: fix log trace 2026-01-11 15:39:58 +08:00
fe9bda0e7b update ufo 2026-01-11 14:54:13 +08:00
16c84c3dca modules.packages.vscode: Disable terminal suggestions 2026-01-11 14:14:34 +08:00
3336eeca78 modules.packages.minimal: switch yq to yq-go 2026-01-10 19:52:11 +08:00
d22da25c2b modified.packages.vscode: add ms-vscode.cpptools 2026-01-10 18:45:02 +08:00
40aa569024 devices.hwang: init 2026-01-09 15:16:48 +08:00
5f36b073a7 modified.packages.desktop: remove some packages 2026-01-09 14:53:38 +08:00
c707d8e168 packages.missgram: Update parse mode to Markdown 2026-01-09 14:43:58 +08:00
758d86262c packages.missgram: enable notification 2026-01-08 22:33:13 +08:00
08e3440742 Merge branch 'missgram' into production 2026-01-08 19:15:14 +08:00
5e1c07931e packages.missgram: fix 2026-01-08 19:15:00 +08:00
f1b762c959 packages.biu: fix inline 2026-01-08 19:11:45 +08:00
2079a35e67 fix ufo 2026-01-08 12:37:22 +08:00
438c569c2f modules.system.gui: add more keybindings for niri 2026-01-08 10:30:43 +08:00
7a50f12129 devices.nas: add new device 2026-01-07 12:07:47 +08:00
0389536c9d flake: not use submodule 2026-01-06 21:17:52 +08:00
163faa0db3 modules.system.fileSystems.impermanence: add dms cache dirs to impermanence 2026-01-06 21:10:22 +08:00
6d11d3232f flake.lib.buildNixpkgsConfig: downgrade typst 2026-01-06 20:54:48 +08:00
48cd33e478 update typst 2026-01-06 17:39:29 +08:00
65bf14958b modules.packages.yazi: add compress plugin and keymap 2026-01-06 13:59:19 +08:00
26371c3076 modules.services.gitea: disable AI web crawler prevention 2026-01-06 12:44:54 +08:00
f0cc24522e modules.services.gitea: fix 2026-01-06 12:38:43 +08:00
411a276d34 modules.system.grub: wait indefinitely on desktops 2026-01-06 12:31:34 +08:00
3b2ed6d850 modules.system.systemd: handle lid switch 2026-01-06 12:31:34 +08:00
e580d7faa2 flake: add ufo as submodule 2026-01-06 12:31:34 +08:00
31 changed files with 278 additions and 160 deletions

6
.gitmodules vendored
View File

@@ -1,6 +0,0 @@
[submodule "nixpkgs"]
path = nixpkgs
url = https://github.com/CHN-beta/nixpkgs.git
[submodule "packages/ufo"]
path = packages/ufo
url = https://git.chn.moe/chn/ufo.git

View File

@@ -7,6 +7,7 @@ let devices =
"/dev/disk/by-partlabel/nas-root2".mapper = "root2";
"/dev/disk/by-partlabel/nas-root3" = { mapper = "root3"; ssd = true; };
"/dev/disk/by-partlabel/nas-root4" = { mapper = "root4"; ssd = true; };
"/dev/disk/by-partlabel/nas-root5".mapper = "root5";
"/dev/disk/by-partlabel/nas-swap" = { mapper = "swap"; ssd = true; };
"/dev/disk/by-partlabel/nas-ssd1" = { mapper = "ssd1"; ssd = true; };
"/dev/disk/by-partlabel/nas-ssd2" = { mapper = "ssd2"; ssd = true; };

18
devices/hwang/default.nix Normal file
View File

@@ -0,0 +1,18 @@
# sudo nix build --store 'local?store=/data/gpfs01/hwang/.nix/store&state=/data/gpfs01/hwang/.nix/state&log=/data/gpfs01/hwang/.nix/log' .#hwang
# sudo nix-store --store 'local?store=/data/gpfs01/hwang/.nix/store&state=/data/gpfs01/hwang/.nix/state&log=/data/gpfs01/hwang/.nix/log' -qR ./result | grep -Fxv -f <(ssh hwang find .nix/store -maxdepth 1 -exec realpath '{}' '\;') | sudo xargs nix-store --store 'local?store=/data/gpfs01/hwang/.nix/store&state=/data/gpfs01/hwang/.nix/state&log=/data/gpfs01/hwang/.nix/log' --export | pv > hwang.nar
# cat hwang.nar | nix-store --import
{ inputs, localLib }:
let
pkgs = import inputs.nixpkgs (localLib.buildNixpkgsConfig
{
inputs = { inherit (inputs.nixpkgs) lib; topInputs = inputs; };
nixpkgs = { march = "haswell"; nixRoot = "/data/gpfs01/hwang/.nix"; nixos = false; };
});
hwang = pkgs.symlinkJoin
{
name = "hwang";
paths = with pkgs; [ pv localPackages.vasp.intel glibc ];
postBuild = "echo ${inputs.self.rev or "dirty"} > $out/.version";
passthru = { inherit pkgs; archive = pkgs.closureInfo { rootPaths = [ hwang.drvPath ]; }; };
};
in hwang

View File

@@ -21,7 +21,10 @@ inputs:
swap = [ "/dev/mapper/swap" ];
# TODO: snapshot should take place just before switching root
rollingRootfs.waitDevices =
[ "/dev/mapper/root2" "/dev/mapper/root3" "/dev/mapper/root4" "/dev/mapper/ssd1" "/dev/mapper/ssd2" ];
[
"/dev/mapper/root2" "/dev/mapper/root3" "/dev/mapper/root4" "/dev/mapper/root5"
"/dev/mapper/ssd1" "/dev/mapper/ssd2"
];
};
initrd.sshd = {};
nixpkgs.march = "alderlake";

View File

@@ -18,9 +18,10 @@ inputs:
"/dev/mapper/tf1" =
{
"/" = "/nix/tf";
"/nix/remote/jykang" = "/data/gpfs01/jykang/.nix";
"/nix/remote/xmuhk" = "/public/home/xmuhk/.nix";
"/nix/remote/jykang" = "/data/gpfs01/jykang/.nix";
"/nix/remote/wlin" = "/data/gpfs01/wlin/.nix";
"/nix/remote/hwang" = "/data/gpfs01/hwang/.nix";
};
};
nfs."nas.ts.chn.moe:/" = { mountPoint = "/nix/remote/nas"; mountBeforeSwitch = false; };

36
flake.lock generated
View File

@@ -1161,14 +1161,19 @@
},
"nixpkgs_3": {
"locked": {
"path": "nixpkgs",
"type": "path"
"lastModified": 1767694262,
"narHash": "sha256-xpFtS6JGpwaDVqXWEYDfoLb05oLEAL1VE2GF9x8smDk=",
"owner": "CHN-beta",
"repo": "nixpkgs",
"rev": "ae0b0157cbfee59adb0d6211ea6e47299a173cd5",
"type": "github"
},
"original": {
"path": "nixpkgs",
"type": "path"
},
"parent": []
"owner": "CHN-beta",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixvirt": {
"inputs": {
@@ -1491,6 +1496,7 @@
"sticker": "sticker",
"stickerpicker": "stickerpicker",
"tgbot-cpp": "tgbot-cpp",
"ufo": "ufo",
"v-sim": "v-sim",
"vaspberry": "vaspberry",
"winapps": "winapps",
@@ -1776,6 +1782,24 @@
"type": "github"
}
},
"ufo": {
"flake": false,
"locked": {
"lastModified": 1768114392,
"lfs": true,
"narHash": "sha256-H2vC6gA4oBBs4ZYSQAxI9VmviCcT9U9+xqNW0Q/N5Jw=",
"ref": "refs/heads/main",
"rev": "b130375ff8c5e4c02febdf3821ddeb43b6cbd89c",
"revCount": 89,
"type": "git",
"url": "https://git.chn.moe/chn/ufo.git"
},
"original": {
"lfs": true,
"type": "git",
"url": "https://git.chn.moe/chn/ufo.git"
}
},
"v-sim": {
"flake": false,
"locked": {

View File

@@ -3,8 +3,7 @@
inputs =
{
self.submodules = true;
nixpkgs.url = ./nixpkgs;
nixpkgs.url = "github:CHN-beta/nixpkgs/nixos-25.11";
nixpkgs-2505.url = "github:CHN-beta/nixpkgs/nixos-25.05";
nixpkgs-2411.url = "github:CHN-beta/nixpkgs/nixos-24.11";
nixpkgs-2311.url = "github:CHN-beta/nixpkgs/nixos-23.11";
@@ -66,6 +65,7 @@
sqlgen = { url = "git+https://github.com/getml/sqlgen?submodules=1"; flake = false; };
reflectcpp = { url = "git+https://github.com/getml/reflect-cpp?submodules=1"; flake = false; };
linux-asus = { url = "github:CHN-beta/linux-g14/6.18"; flake = false; };
ufo = { url = "git+https://git.chn.moe/chn/ufo.git?lfs=1"; flake = false; };
};
outputs = inputs: let localLib = import ./flake/lib inputs.nixpkgs.lib; in

View File

@@ -79,6 +79,7 @@ in platformConfig //
buildInputs = prev.buildInputs or [] ++ [ final.localPackages.lsf final.libnsl ];
});
cpptrace = prev.cpptrace.overrideAttrs (prev: { doCheck = !final.stdenv.hostPlatform.isStatic; });
typst = final.pkgs-2505.typst;
}
// (
let
@@ -104,6 +105,7 @@ in platformConfig //
})
];
};
pkgs-2505 = "nixpkgs-2505";
};
packages = name:
let flakeSource = inputs.topInputs.${source.${name}.source or source.${name}};

View File

@@ -29,6 +29,7 @@
jykang = import ../devices/jykang { inherit inputs localLib; };
wlin = import ../devices/wlin { inherit inputs localLib; };
xmuhk = import ../devices/xmuhk { inherit inputs localLib; };
hwang = import ../devices/hwang { inherit inputs localLib; };
src =
let getDrv = x:
if pkgs.lib.isDerivation x then [ x ]

View File

@@ -15,7 +15,7 @@ inputs:
[
# system management
# TODO: module should add yubikey-touch-detector into path
gparted wayland-utils clinfo mesa-demos vulkan-tools dracut yubikey-touch-detector btrfs-assistant snapper-gui
gparted wayland-utils clinfo mesa-demos vulkan-tools dracut yubikey-touch-detector btrfs-assistant
cpu-x wl-mirror geekbench xpra
(
writeShellScriptBin "xclip"
@@ -60,19 +60,15 @@ inputs:
# matplot++ needs old gnuplot
pkgs-2311.gnuplot
# math, physics and chemistry
octaveFull ovito localPackages.vesta localPackages.v-sim jmol mpi geogebra6 localPackages.ufo
octaveFull ovito localPackages.vesta localPackages.v-sim mpi geogebra6 localPackages.ufo
(quantum-espresso.override { stdenv = gcc14Stdenv; gfortran = gfortran14; })
pkgs-2311.hdfview numbat qalculate-qt
# virtualization
virt-viewer bottles wineWowPackages.stagingFull genymotion playonlinux
pkgs-2311.hdfview
# media
nur-xddxdd.svp
# for kdenlive auto subtitle
openai-whisper
# daily management
activitywatch super-productivity
# game
lutris
super-productivity
];
_pythonPackages = [(pythonPackages: with pythonPackages;
[

View File

@@ -9,7 +9,7 @@ inputs:
_packages = with inputs.pkgs;
[
# basic tools
beep dos2unix gnugrep pv tmux screen parallel tldr cowsay jq yq ipfetch localPackages.pslist
beep dos2unix gnugrep pv tmux screen parallel tldr cowsay jq yq-go ipfetch localPackages.pslist
fastfetch reptyr duc ncdu progress libva-utils ksh neofetch dateutils glib cryptsetup
# lsxx
pciutils usbutils lshw util-linux lsof dmidecode lm_sensors hwloc acpica-tools ethtool

View File

@@ -26,8 +26,7 @@ inputs:
[
"github.copilot" "github.copilot-chat" "github.github-vscode-theme"
"intellsmi.comment-translate"
"ms-vscode.cmake-tools" "ms-vscode.cpptools-extension-pack" "ms-vscode.hexeditor"
"ms-vscode.remote-explorer"
"ms-vscode.hexeditor" "ms-vscode.remote-explorer"
"ms-vscode-remote.remote-ssh"
"donjayamanne.githistory" "fabiospampinato.vscode-diff"
"llvm-vs-code-extensions.vscode-clangd" "ms-ceintl.vscode-language-pack-zh-hans"
@@ -65,6 +64,9 @@ inputs:
"ms-toolsai.datawrangler"
# nushell
"TheNuProjectContributors.vscode-nushell-lang"
# C/C++
"ms-vscode.cmake-tools" "ms-vscode.cpptools-extension-pack" "ms-vscode.cpptools" "coolchyni.beyond-debug"
"vadimcn.vscode-lldb"
];
keybindings =
[
@@ -335,6 +337,8 @@ inputs:
"tinymist.preview.partialRendering" = false;
"tinymist.preview.refresh" = "onSave";
"workbench.secondarySideBar.defaultVisibility" = "hidden";
# disable terminal suggestions
"terminal.integrated.suggest.enabled" = false;
};
};
};

View File

@@ -1,4 +1,4 @@
inputs:
{ pkgs, ... }:
{
config =
{
@@ -8,7 +8,12 @@ inputs:
config.programs.yazi =
{
enable = true;
keymap.mgr.append_keymap = [{ on = "T"; run = "shell --orphan ghostty"; }];
keymap.mgr.append_keymap =
[
{ on = "T"; run = "shell --orphan ghostty"; }
{ on = [ "c" "a" "a" ]; run = "plugin compress"; }
];
plugins = { inherit (pkgs.yaziPlugins) compress; };
};
}];
};

View File

@@ -59,19 +59,22 @@ inputs:
};
# prevent AI web crawlers
# https://her.esy.fun/posts/0031-how-i-protect-my-forgejo-instance-from-ai-web-crawlers/index.html
nginx.virtualHosts."https:${gitea.hostname}".locations."/".extraConfigPre =
''
if ($http_user_agent ~* "git/|git-lfs/") {
set $bypass_cookie 1;
}
if ($cookie_Yogsototh_opens_the_door = "1") {
set $bypass_cookie 1;
}
if ($bypass_cookie != 1) {
add_header Content-Type text/html always;
return 418 '<script>document.cookie = "Yogsototh_opens_the_door=1; Path=/;"; window.location.reload();</script>';
}
'';
# nginx.virtualHosts."https:${gitea.hostname}".locations."/".extraConfigPre =
# ''
# if ($http_user_agent ~* "git/|git-lfs/") {
# set $bypass_cookie 1;
# }
# if ($cookie_Yogsototh_opens_the_door = "1") {
# set $bypass_cookie 1;
# }
# if ($request_method != "GET") {
# set $bypass_cookie 1;
# }
# if ($bypass_cookie != 1) {
# add_header Content-Type text/html always;
# return 418 '<script>document.cookie = "Yogsototh_opens_the_door=1; Path=/;"; window.location.reload();</script>';
# }
# '';
};
nixos =
{

View File

@@ -69,6 +69,8 @@ inputs:
"bin" "Desktop" "Documents" "Downloads" "Music" "Pictures" "repo" "share" "Public" "Videos" ".config"
".local" ".ecdata" { directory = ".mozilla/firefox/default"; mode = "0700"; } ".steam" ".zotero"
"Zotero" ".thunderbird"
# dms 将剪贴板历史数据和主题的一些设置存放在这里
".cache/dms-clipboard" ".cache/DankMaterialShell"
];
})
# 对于集群的工作节点,挂载一些本来由 home-manager 生成的文件,以及一些用来存放 home-manager 生成文件的目录

View File

@@ -4,7 +4,6 @@ inputs:
{
type = types.nullOr (types.submodule { options =
{
timeout = mkOption { type = types.int; default = 15; };
windowsEntries = mkOption { type = types.attrsOf types.nonEmptyStr; default = {}; };
# "efi" using efi, "efiRemovable" using efi with install grub removable, or dev path like "/dev/sda" using bios
installDevice = mkOption { type = types.str; default = "efi"; };
@@ -15,9 +14,13 @@ inputs:
(inputs.lib.mkMerge
[
# general settings
{ boot.loader.grub = { enable = true; useOSProber = false; }; }
# grub timeout
{ boot.loader.timeout = grub.timeout; }
{
boot.loader =
{
grub = { enable = true; useOSProber = false; };
timeout = if inputs.config.nixos.model.type == "desktop" then null else 15;
};
}
# grub install
{
boot.loader =

View File

@@ -71,6 +71,10 @@ inputs:
"Mod+WheelScrollUp" = { action.focus-column-left = {}; cooldown-ms = 50; };
"Mod+Left".action.focus-column-left = {};
"Mod+Right".action.focus-column-right = {};
"Ctrl+Mod+Left".action.move-column-left = {};
"Ctrl+Mod+Right".action.move-column-right = {};
"Mod+Up".action.focus-workspace-up = {};
"Mod+Down".action.focus-workspace-down = {};
"Mod+MouseMiddle".action.close-window = {};
"Mod+L".action.spawn = [ "dms" "ipc" "lock" "lock" ];
"Mod+W".action.move-workspace-to-monitor-next = {};

View File

@@ -3,11 +3,7 @@ inputs:
config =
{
# only preserve the last 7 days of logs
services =
{
journald.extraConfig = "MaxRetentionSec=7d";
logind.settings.Login.HandleLidSwitch = "ignore";
};
services.journald.extraConfig = "MaxRetentionSec=7d";
systemd =
{
settings.Manager =

Submodule nixpkgs deleted from d8ca282fc0

View File

@@ -19,7 +19,7 @@ namespace biu
namespace common
{
std::size_t hash(auto&&... objs);
[[gnu::always_inline]] void unused(auto&&...);
[[gnu::always_inline]] inline void unused(auto&&...);
[[noreturn]] void block_forever();
bool is_interactive();

View File

@@ -41,10 +41,10 @@ namespace biu
protected: const std::chrono::time_point<std::chrono::steady_clock> CreateTime_;
// call log<Debug>("create {type} at {address}.");
protected: [[gnu::always_inline]] ObjectMonitor();
protected: [[gnu::noinline]] inline ObjectMonitor();
// call log<Debug>("destroy {type} at {address} after {duration} ms.");
protected: [[gnu::always_inline]] virtual ~ObjectMonitor();
protected: [[gnu::noinline]] inline virtual ~ObjectMonitor();
};
template <typename T> friend class ObjectMonitor;
@@ -65,24 +65,26 @@ namespace biu
// if sizeof...(Param) > 0, call log<Debug>("begin function with {arguments}.");
// else call log<Debug>("begin function.");
public: template <typename... Param> [[gnu::always_inline]] explicit Guard(Param&&... param);
public: template <typename... Param> explicit Guard(Param&&... param);
// call log<Debug>("end function after {duration} ms.")
public: [[gnu::always_inline]] inline virtual ~Guard();
public: [[gnu::noinline]] inline virtual ~Guard();
// call log<Debug>("reached after {duration} ms.")
public: [[gnu::always_inline]] inline void operator()() const;
public: [[gnu::noinline]] inline void operator()() const;
// call log<Debug>("return {return} after {duration} ms.")
public: template <typename T> [[gnu::always_inline]] T rtn(T&& value) const;
public: template <typename T> [[gnu::noinline]] T rtn(T&& value) const;
// print the following message if LoggerConfig_ is set and the level is higher than the level of the
// LoggerConfig_
// [ {time} {thread} {indent} {filename}:{line} {function_name} ] {message}
public: template <Level L> [[gnu::always_inline]] void log(const std::string& message) const;
public: [[gnu::always_inline]] inline void error(const std::string& message) const;
public: [[gnu::always_inline]] inline void info(const std::string& message) const;
public: [[gnu::always_inline]] inline void debug(const std::string& message) const;
// All directly called log functions should not be inlined to ensure correct stacktrace information
// all not directly called log functions should be inlined to align stacktrace depth (always 1)
protected: template <Level L> [[gnu::noinline]] void log_(const std::string& message) const;
public: [[gnu::noinline]] inline void error(const std::string& message) const;
public: [[gnu::noinline]] inline void info(const std::string& message) const;
public: [[gnu::noinline]] inline void debug(const std::string& message) const;
};
friend class Guard;

View File

@@ -46,12 +46,12 @@ namespace biu
{
Guard guard;
Objects_.lock()->emplace(this, nameof::nameof_full_type<T>());
guard.debug("create {} at {}."_f(nameof::nameof_full_type<T>(), fmt::ptr(this)));
guard.log_<Level::Debug>("create {} at {}."_f(nameof::nameof_full_type<T>(), fmt::ptr(this)));
}
template <typename T> Logger::ObjectMonitor<T>::~ObjectMonitor()
{
Guard guard;
guard.log<Level::Debug>("destroy {} at {} after {} ms."_f
guard.log_<Level::Debug>("destroy {} at {} after {} ms."_f
(
nameof::nameof_full_type<T>(), fmt::ptr(this),
std::chrono::duration_cast<std::chrono::milliseconds>
@@ -102,13 +102,13 @@ namespace biu
else return "({})"_f(nameof::nameof_full_type<T>());
};
if constexpr (sizeof...(Param) > 0)
debug("begin function with {{{}}}."_f(fmt::join({try_format(std::forward<Param>(param))...}, ", ")));
else debug("begin function.");
log_<Level::Debug>("begin function with {{{}}}."_f(fmt::join({try_format(std::forward<Param>(param))...}, ", ")));
else log_<Level::Debug>("begin function.");
}
Logger::Guard::~Guard()
{
debug("end function after {} ms."_f(get_time_ms()));
log_<Level::Debug>("end function after {} ms."_f(get_time_ms()));
Indent_--;
auto&& lock = Threads_.lock();
if (auto thread_id = get_thread_id(); lock->contains(thread_id))
@@ -116,14 +116,14 @@ namespace biu
else [[unlikely]]
error("{:08x} not found in Logger::Threads."_f(thread_id % std::numeric_limits<std::uint64_t>::max()));
}
void Logger::Guard::operator()() const { debug("reached after {} ms."_f(get_time_ms())); }
template <Logger::Level L> void Logger::Guard::log(const std::string& message) const
void Logger::Guard::operator()() const { log_<Level::Debug>("reached after {} ms."_f(get_time_ms())); }
template <Logger::Level L> void Logger::Guard::log_(const std::string& message) const
{
if (auto&& lock = LoggerConfig_.lock(); lock->Level >= L)
{
static_assert(std::same_as<std::size_t, std::uint64_t>);
auto time = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
auto frame = cpptrace::stacktrace::current(0, 1).frames[0];
auto frame = cpptrace::stacktrace::current(2, 1).frames[0];
# ifdef BIU_LOGGER_SOURCE_ROOT
auto source_root = std::string_view(BIU_LOGGER_SOURCE_ROOT "/");
auto source_file = frame.filename.starts_with(source_root) ?
@@ -143,13 +143,13 @@ namespace biu
) << std::flush;
}
}
void Logger::Guard::error(const std::string& message) const { log<Level::Error>(message); }
void Logger::Guard::info(const std::string& message) const { log<Level::Info>(message); }
void Logger::Guard::debug(const std::string& message) const { log<Level::Debug>(message); }
void Logger::Guard::error(const std::string& message) const { log_<Level::Error>(message); }
void Logger::Guard::info(const std::string& message) const { log_<Level::Info>(message); }
void Logger::Guard::debug(const std::string& message) const { log_<Level::Debug>(message); }
template <typename T> inline T Logger::Guard::rtn(T&& value) const
template <typename T> T Logger::Guard::rtn(T&& value) const
{
debug("return {} after {} ms."_f(std::forward<T>(value), get_time_ms()));
log_<Level::Debug>("return {} after {} ms."_f(std::forward<T>(value), get_time_ms()));
return std::forward<T>(value);
}

View File

@@ -2,4 +2,6 @@
int main()
{
biu::Logger::Guard log("test", nullptr, std::ofstream());
log.info("hello world");
return 0;
}

View File

@@ -72,7 +72,7 @@ inputs: rec
sqlite-orm = inputs.pkgs.callPackage ./sqlite-orm.nix { src = inputs.topInputs.sqlite-orm; };
mkPnpmPackage = inputs.pkgs.callPackage ./mkPnpmPackage.nix {};
sbatch-tui = inputs.pkgs.callPackage ./sbatch-tui { inherit biu; };
ufo = inputs.pkgs.callPackage ./ufo { inherit biu matplotplusplus; tbb = inputs.pkgs.tbb_2022; };
ufo = inputs.pkgs.callPackage inputs.topInputs.ufo { inherit biu matplotplusplus; tbb = inputs.pkgs.tbb_2022; };
chn-bsub = inputs.pkgs.callPackage ./chn-bsub { inherit biu; };
py4vasp = inputs.pkgs.python3Packages.callPackage ./py4vasp.nix { src = inputs.topInputs.py4vasp; };
pocketfft = inputs.pkgs.callPackage ./pocketfft.nix { src = inputs.topInputs.pocketfft; };

View File

@@ -12,10 +12,11 @@ endif()
find_package(biu REQUIRED)
find_package(httplib REQUIRED)
find_package(sqlgen REQUIRED)
find_package(nlohmann_json REQUIRED)
add_executable(missgram src/main.cpp src/db.cpp src/tg.cpp)
target_include_directories(missgram PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(missgram PRIVATE biu::biu httplib::httplib sqlgen::sqlgen)
target_link_libraries(missgram PRIVATE biu::biu httplib::httplib sqlgen::sqlgen nlohmann_json::nlohmann_json)
target_compile_features(missgram PRIVATE cxx_std_23)
if(DEFINED MISSGRAM_CONFIG_FILE)
target_compile_definitions(missgram PRIVATE MISSGRAM_CONFIG_FILE="${MISSGRAM_CONFIG_FILE}")
@@ -23,6 +24,15 @@ endif()
target_compile_definitions(missgram PRIVATE BIU_LOGGER_SOURCE_ROOT="${CMAKE_CURRENT_SOURCE_DIR}")
install(TARGETS missgram RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
add_executable(tg-test EXCLUDE_FROM_ALL src/tg.cpp src/tg-test.cpp)
target_include_directories(tg-test PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(tg-test PRIVATE biu::biu httplib::httplib nlohmann_json::nlohmann_json)
target_compile_features(tg-test PRIVATE cxx_std_23)
if(DEFINED MISSGRAM_CONFIG_FILE)
target_compile_definitions(tg-test PRIVATE MISSGRAM_CONFIG_FILE="${MISSGRAM_CONFIG_FILE}")
endif()
target_compile_definitions(tg-test PRIVATE BIU_LOGGER_SOURCE_ROOT="${CMAKE_CURRENT_SOURCE_DIR}")
get_property(ImportedTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
message("Imported targets: ${ImportedTargets}")
message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")

View File

@@ -1,8 +1,8 @@
{ lib, stdenv, cmake, pkg-config, biu, configFile ? null, httplib, sqlgen }: stdenv.mkDerivation
{ lib, stdenv, cmake, pkg-config, biu, configFile ? null, httplib, sqlgen, nlohmann_json }: stdenv.mkDerivation
{
name = "missgram";
src = ./.;
buildInputs = [ biu httplib sqlgen ];
buildInputs = [ biu httplib sqlgen nlohmann_json ];
nativeBuildInputs = [ cmake pkg-config ];
cmakeFlags = lib.optional (configFile != null) [ "-DMISSGRAM_CONFIG_FILE=${configFile}" ];
}

View File

@@ -10,7 +10,7 @@ namespace missgram
std::int16_t ServerPort;
std::string dbPassword;
} inline config;
struct File { std::string url; bool is_photo; bool should_hidden; };
struct File { std::string name, url, type; bool isSensitive; };
void db_write(std::string misskey_note, std::int32_t telegram_message_id);
std::optional<std::int32_t> db_read(std::string misskey_note);

View File

@@ -39,7 +39,6 @@ int main()
struct Renote { std::string id; };
std::optional<Renote> renote;
bool localOnly;
struct File { bool isSensitive; std::string url; std::string type; };
std::vector<File> files;
};
std::optional<Note> note;
@@ -66,7 +65,7 @@ int main()
// 否则(引用或普通帖子)
else
{
text = *content.body.note->text;
text = content.body.note->text.value_or("");
// 如果有引用,则需要查找被引用的帖子是否已经被转发过,若是则直接回复被转发的消息。
// 如果没有被转发过,则在开头附上链接
if (content.body.note->renote)
@@ -82,21 +81,9 @@ int main()
text += "\n[在联邦宇宙查看]({}/notes/{})"_f(content.server, content.body.note->id);
}
// 接下来整理要转发的文件
auto files = content.body.note->files | ranges::views::transform([](auto&& file) -> File
{
return File
{
.url = file.url,
.is_photo = file.type.starts_with("image/"),
.should_hidden = file.isSensitive
};
}) | ranges::to_vector;
log();
// 异步发送消息
std::thread([text, note_id = content.body.note->id, reply_id, files]
std::thread([text, note_id = content.body.note->id, reply_id,
files = content.body.note->files]
{
auto message_id = tg_send(text, reply_id, files);
if (message_id) db_write(note_id, *message_id);

View File

@@ -0,0 +1,25 @@
# include <missgram.hpp>
# ifndef MISSGRAM_CONFIG_FILE
# define MISSGRAM_CONFIG_FILE "./config.yaml"
# endif
int main()
{
using namespace biu::literals;
using namespace missgram;
biu::Logger::Guard log;
config = YAML::LoadFile(MISSGRAM_CONFIG_FILE).as<Config>();
// tg_send("aaaa", std::nullopt, {});
// tg_send("aaaa", std::nullopt, {{"IMG20241013173523.jpg", "https://xn--s8w913fdga.chn.moe/files/3dd41113-4df5-4f34-a825-e4137d146172", "image/jpeg", false}});
tg_send("aaaa", std::nullopt,
{
{"2026-01-07 22-02-22 1.png", "https://xn--s8w913fdga.chn.moe/files/a23d13ea-de37-4907-9d54-66417d7e0e36", "image/png", false}
});
// tg_send("aaaa", std::nullopt,
// {
// {"IMG20241013173523.jpg", "https://xn--s8w913fdga.chn.moe/files/// 3dd41113-4df5-4f34-a825-e4137d146172", "image/jpeg", true},
// {"2026-01-07 22-02-22 1.png", "https://xn--s8w913fdga.chn.moe/files/// a23d13ea-de37-4907-9d54-66417d7e0e36", "image/png", false}
// });
}

View File

@@ -1,5 +1,6 @@
# include <missgram.hpp>
# include <tgbot/tgbot.h>
# include <httplib.h>
# include <nlohmann/json.hpp>
std::optional<std::int32_t> missgram::tg_send
(std::string text, std::optional<std::int32_t> replyId, std::vector<File> files)
@@ -7,84 +8,120 @@ std::optional<std::int32_t> missgram::tg_send
using namespace biu::literals;
biu::Logger::Guard log;
// 整理要发送的信息
TgBot::Bot bot(config.TelegramBotToken);
std::shared_ptr<TgBot::ReplyParameters> reply;
if (replyId) reply = std::make_shared<TgBot::ReplyParameters>(*replyId, config.TelegramChatId);
auto attachs = files
| ranges::views::transform([&](auto&& file) -> TgBot::InputMedia::Ptr
{
if (file.is_photo)
{
auto pic = std::make_shared<TgBot::InputMediaPhoto>();
pic->media = file.url;
pic->hasSpoiler = file.should_hidden;
return pic;
}
else
{
auto doc = std::make_shared<TgBot::InputMediaDocument>();
doc->media = file.url;
return doc;
}
})
| ranges::to_vector;
// 多次尝试运行函数直到成功或达到最大尝试次数5次
auto try_run = [&](auto&& func) -> std::optional<std::int32_t>
auto try_run = [&](auto&& func) -> std::optional<decltype(func())>
{
auto retry_delay = 1s;
int attempts = 0;
while (attempts < 5)
{
TgBot::Message::Ptr message;
biu::Logger::try_exec([&] { message = func(); });
if (message) return message->messageId;
std::optional<decltype(func())> result;
biu::Logger::try_exec([&] { result = func(); });
if (result) return result;
std::this_thread::sleep_for(retry_delay);
retry_delay *= 2;
attempts++;
}
return std::nullopt;
return {};
};
// 如果没有附件,使用 sendMessage 发送文本消息
if (attachs.empty()) return try_run([&] { return bot.getApi().sendMessage
(
config.TelegramChatId, text, nullptr, reply, nullptr,
"MarkdownV2"
);});
// 如果只有一个附件并且是图片,使用 sendPhoto 发送
else if (attachs.size() == 1 && files[0].is_photo) return try_run([&]
// 下载一个 https 资源到内存
auto download = [&](const std::string& url) -> std::string
{
return bot.getApi().sendPhoto
(
config.TelegramChatId, files[0].url, text, reply,
nullptr, "MarkdownV2", false, {}, 0, false, files[0].should_hidden
);
});
// 如果有多个附件,使用 sendMediaGroup 分两条消息发送,返回第一条的 id
std::regex https_regex(R"(https://([^/]+)(/.+))");
std::smatch match;
if (!std::regex_match(url, match, https_regex))
throw std::runtime_error("Only https URLs are supported");
httplib::SSLClient cli(match[1].str());
auto res = cli.Get(match[2].str());
if (res && res->status == 200) return res->body;
else throw std::runtime_error("Failed to download file from " + url);
};
// 下载要发送的文件
std::vector<std::string> file_contents;
for (const auto& file : files)
{
auto content = try_run([&] { return download(file.url); });
if (!content) throw std::runtime_error("Failed to download file from " + file.url);
file_contents.push_back(std::move(*content));
}
// 准备要发送的请求
httplib::UploadFormDataItems items;
std::string method;
if (files.empty())
{
method = "sendMessage";
items.push_back({"text", text});
items.push_back({"parse_mode", "Markdown"});
items.push_back({"link_preview_options",
[]{ nlohmann::json j; j["is_disabled"] = true; return j.dump(); }()});
}
else if (files.size() == 1)
{
auto is_photo = files[0].type.starts_with("image/");
method = is_photo ? "sendPhoto" : "sendDocument";
items.push_back
({
is_photo ? "photo" : "document",
file_contents[0], files[0].name, files[0].type
});
items.push_back({"caption", text});
items.push_back({"parse_mode", "Markdown"});
if (is_photo && files[0].isSensitive) items.push_back({"has_spoiler", "True"});
}
else
{
auto message = try_run([&] { return bot.getApi().sendMessage
(
config.TelegramChatId, text, nullptr, reply, nullptr,
"MarkdownV2"
);});
if (message)
method = "sendMediaGroup";
auto all_photo = ranges::all_of(files,
[](auto&& file) { return file.type.starts_with("image/"); });
nlohmann::json media_group = files | ranges::views::enumerate | ranges::views::transform([&](auto&& file)
{
auto message2 = try_run([&] -> TgBot::Message::Ptr
nlohmann::json params;
if (all_photo)
{
auto msg = bot.getApi().sendMediaGroup
(
config.TelegramChatId, attachs, false,
std::make_shared<TgBot::ReplyParameters>(*message, config.TelegramChatId)
);
if (msg.empty() || !ranges::all_of(msg, [](auto&& m) { return bool(m); }))
return nullptr;
else return msg[0];
});
if (!message2) return {};
params["type"] = "photo";
if (file.second.isSensitive) params["has_spoiler"] = true;
}
else params["type"] = "document";
params["media"] = "attach://media-{}"_f(file.first);
log.debug("Prepared media {}: {}"_f(file.first, params.dump()));
if (file.first == 0) { params["caption"] = text; params["parse_mode"] = "Markdown"; }
return params;
}) | ranges::to_vector;
items.push_back({"media", media_group.dump()});
for (int i = 0; i < files.size(); i++) items.push_back
({"media-{}"_f(i), file_contents[i], files[i].name, files[i].type});
}
items.push_back({"chat_id", std::to_string(config.TelegramChatId)});
if (replyId) items.push_back({"reply_parameters", [&]
{
nlohmann::json j;
j["message_id"] = *replyId;
j["chat_id"] = config.TelegramChatId;
return j.dump();
}()});
httplib::Client cli("https://api.telegram.org");
auto result = cli.Post("/bot{}/{}"_f(config.TelegramBotToken, method), items);
log.debug("{} {} {}"_f(result->status, result->body, result->headers));
if (result && result->status == 200)
{
auto json = nlohmann::json::parse(result->body);
// 测试 js["result"]["message_id"] 是否存在且为整数
if (json.contains("result") && json["result"].contains("message_id")
&& json["result"]["message_id"].is_number_integer())
return json["result"]["message_id"].get<std::int32_t>();
else
{
log.error("Telegram API error: {}"_f(json.dump()));
return {};
}
return message;
}
else
{
log.error("HTTP error: {}"_f(result ? std::to_string(result->status) : "No response"));
return {};
}
}

Submodule packages/ufo deleted from 19736049c9