├── .github └── workflows │ └── release.yml ├── .gitignore ├── .goreleaser.yaml ├── DISCLAIMER.md ├── LICENSE ├── Makefile ├── README.md ├── cmd └── chatlog │ ├── cmd_decrypt.go │ ├── cmd_dumpmemory.go │ ├── cmd_key.go │ ├── cmd_server.go │ ├── cmd_version.go │ ├── log.go │ └── root.go ├── docs ├── mcp.md └── prompt.md ├── go.mod ├── go.sum ├── internal ├── chatlog │ ├── app.go │ ├── conf │ │ ├── config.go │ │ └── service.go │ ├── ctx │ │ └── context.go │ ├── database │ │ └── service.go │ ├── http │ │ ├── route.go │ │ ├── service.go │ │ └── static │ │ │ └── index.htm │ ├── manager.go │ ├── mcp │ │ ├── const.go │ │ └── service.go │ └── wechat │ │ └── service.go ├── errors │ ├── errors.go │ ├── http_errors.go │ ├── middleware.go │ ├── os_errors.go │ ├── wechat_errors.go │ └── wechatdb_errors.go ├── mcp │ ├── error.go │ ├── initialize.go │ ├── jsonrpc.go │ ├── mcp.go │ ├── prompt.go │ ├── resource.go │ ├── session.go │ ├── sse.go │ ├── stdio.go │ └── tool.go ├── model │ ├── chatroom.go │ ├── chatroom_darwinv3.go │ ├── chatroom_v4.go │ ├── contact.go │ ├── contact_darwinv3.go │ ├── contact_v4.go │ ├── media.go │ ├── media_darwinv3.go │ ├── media_v4.go │ ├── mediamessage.go │ ├── message.go │ ├── message_darwinv3.go │ ├── message_v3.go │ ├── message_v4.go │ ├── session.go │ ├── session_darwinv3.go │ ├── session_v4.go │ └── wxproto │ │ ├── bytesextra.pb.go │ │ ├── bytesextra.proto │ │ ├── packedinfo.pb.go │ │ ├── packedinfo.proto │ │ ├── roomdata.pb.go │ │ └── roomdata.proto ├── ui │ ├── footer │ │ └── footer.go │ ├── form │ │ └── form.go │ ├── help │ │ └── help.go │ ├── infobar │ │ └── infobar.go │ ├── menu │ │ ├── menu.go │ │ └── submenu.go │ └── style │ │ ├── style.go │ │ └── style_windows.go ├── wechat │ ├── decrypt │ │ ├── common │ │ │ └── common.go │ │ ├── darwin │ │ │ ├── v3.go │ │ │ └── v4.go │ │ ├── decryptor.go │ │ ├── validator.go │ │ └── windows │ │ │ ├── v3.go │ │ │ └── v4.go │ ├── key │ │ ├── darwin │ │ │ ├── glance │ │ │ │ ├── glance.go │ │ │ │ ├── sip.go │ │ │ │ └── vmmap.go │ │ │ ├── v3.go │ │ │ └── v4.go │ │ ├── extractor.go │ │ └── windows │ │ │ ├── v3.go │ │ │ ├── v3_others.go │ │ │ ├── v3_windows.go │ │ │ ├── v4.go │ │ │ ├── v4_others.go │ │ │ └── v4_windows.go │ ├── manager.go │ ├── model │ │ └── process.go │ ├── process │ │ ├── darwin │ │ │ └── detector.go │ │ ├── detector.go │ │ └── windows │ │ │ ├── detector.go │ │ │ ├── detector_others.go │ │ │ └── detector_windows.go │ └── wechat.go └── wechatdb │ ├── datasource │ ├── darwinv3 │ │ └── datasource.go │ ├── datasource.go │ ├── dbm │ │ ├── dbm.go │ │ ├── dbm_test.go │ │ └── group.go │ ├── v4 │ │ └── datasource.go │ └── windowsv3 │ │ └── datasource.go │ ├── repository │ ├── chatroom.go │ ├── contact.go │ ├── media.go │ ├── message.go │ ├── repository.go │ └── session.go │ └── wechatdb.go ├── main.go ├── pkg ├── appver │ ├── version.go │ ├── version_darwin.go │ ├── version_others.go │ └── version_windows.go ├── config │ ├── config.go │ └── default.go ├── filecopy │ └── filecopy.go ├── filemonitor │ ├── filegroup.go │ └── filemonitor.go ├── util │ ├── dat2img │ │ └── dat2img.go │ ├── lz4 │ │ └── lz4.go │ ├── os.go │ ├── os_windows.go │ ├── silk │ │ └── silk.go │ ├── strings.go │ ├── time.go │ ├── time_test.go │ └── zstd │ │ └── zstd.go └── version │ └── version.go └── script └── package.sh /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | env: 9 | ENABLE_UPX: 1 10 | 11 | jobs: 12 | release: 13 | name: Release Binary 14 | runs-on: ubuntu-latest 15 | container: 16 | image: goreleaser/goreleaser-cross:v1.24 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - run: git config --global --add safe.directory "$(pwd)" 23 | 24 | - name: Setup Go 25 | uses: actions/setup-go@v4 26 | with: 27 | go-version: '^1.24' 28 | 29 | - name: Cache go module 30 | uses: actions/cache@v3 31 | with: 32 | path: ~/go/pkg/mod 33 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 34 | restore-keys: | 35 | ${{ runner.os }}-go- 36 | 37 | - name: Install UPX 38 | uses: crazy-max/ghaction-upx@v3 39 | with: 40 | install-only: true 41 | 42 | - name: Run GoReleaser 43 | run: goreleaser release --clean 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | ENABLE_UPX: true 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | 24 | # env file 25 | .env 26 | 27 | # syncthing files 28 | .stfolder 29 | 30 | chatlog.exe# Added by goreleaser init: 31 | dist/ 32 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # GoReleaser v2 配置 2 | version: 2 3 | 4 | before: 5 | hooks: 6 | - go mod tidy 7 | 8 | builds: 9 | - id: darwin-amd64 10 | binary: chatlog 11 | env: 12 | - CGO_ENABLED=1 13 | - CC=o64-clang 14 | - CXX=o64-clang++ 15 | goos: 16 | - darwin 17 | goarch: 18 | - amd64 19 | ldflags: 20 | - -s -w -X github.com/sjzar/chatlog/pkg/version.Version={{.Version}} 21 | 22 | - id: darwin-arm64 23 | binary: chatlog 24 | env: 25 | - CGO_ENABLED=1 26 | - CC=oa64-clang 27 | - CXX=oa64-clang++ 28 | goos: 29 | - darwin 30 | goarch: 31 | - arm64 32 | ldflags: 33 | - -s -w -X github.com/sjzar/chatlog/pkg/version.Version={{.Version}} 34 | 35 | - id: windows-amd64 36 | binary: chatlog 37 | env: 38 | - CGO_ENABLED=1 39 | - CC=x86_64-w64-mingw32-gcc 40 | - CXX=x86_64-w64-mingw32-g++ 41 | goos: 42 | - windows 43 | goarch: 44 | - amd64 45 | ldflags: 46 | - -s -w -X github.com/sjzar/chatlog/pkg/version.Version={{.Version}} 47 | 48 | - id: windows-arm64 49 | binary: chatlog 50 | env: 51 | - CGO_ENABLED=1 52 | - CC=/llvm-mingw/bin/aarch64-w64-mingw32-gcc 53 | - CXX=/llvm-mingw/bin/aarch64-w64-mingw32-g++ 54 | goos: 55 | - windows 56 | goarch: 57 | - arm64 58 | ldflags: 59 | - -s -w -X github.com/sjzar/chatlog/pkg/version.Version={{.Version}} 60 | 61 | archives: 62 | - id: default 63 | format: tar.gz 64 | name_template: >- 65 | {{ .ProjectName }}_ 66 | {{- .Version }}_ 67 | {{- .Os }}_ 68 | {{- .Arch }} 69 | format_overrides: 70 | - goos: windows 71 | format: zip 72 | files: 73 | - LICENSE 74 | - README.md 75 | 76 | upx: 77 | - enabled: "{{ .Env.ENABLE_UPX }}" 78 | goos: [darwin, windows] 79 | goarch: [amd64] 80 | compress: best 81 | 82 | checksum: 83 | name_template: 'checksums.txt' 84 | algorithm: sha256 85 | 86 | # 配置 GitHub Release 87 | release: 88 | draft: true 89 | prerelease: auto 90 | mode: replace 91 | -------------------------------------------------------------------------------- /DISCLAIMER.md: -------------------------------------------------------------------------------- 1 | # Chatlog 免责声明 2 | 3 | ## 1. 定义 4 | 5 | 在本免责声明中,除非上下文另有说明,下列术语应具有以下含义: 6 | 7 | - **"本项目"或"Chatlog"**:指本开源软件项目,包括其源代码、可执行程序、文档及相关资源。 8 | - **"开发者"**:指本项目的创建者、维护者及代码贡献者。 9 | - **"用户"**:指下载、安装、使用或以任何方式接触本项目的个人或实体。 10 | - **"聊天数据"**:指通过各类即时通讯软件生成的对话内容及相关元数据。 11 | - **"合法授权"**:指根据适用法律法规,由数据所有者或数据主体明确授予的处理其聊天数据的权限。 12 | - **"第三方服务"**:指由非本项目开发者提供的外部服务,如大型语言模型(LLM) API 服务。 13 | 14 | ## 2. 使用目的与法律遵守 15 | 16 | 本项目仅供学习、研究和个人合法使用。用户须严格遵守所在国家/地区的法律法规使用本工具。任何违反法律法规、侵犯他人合法权益的行为,均与本项目及其开发者无关,相关法律责任由用户自行承担。 17 | 18 | ⚠️ **用户应自行了解并遵守当地有关数据访问、隐私保护、计算机安全和网络安全的法律法规。不同司法管辖区对数据处理有不同的法律要求,用户有责任确保其使用行为符合所有适用法规。** 19 | 20 | ## 3. 授权范围与隐私保护 21 | 22 | - 本工具仅限于处理用户自己合法拥有的聊天数据,或已获得数据所有者明确授权的数据。 23 | - 严禁将本工具用于未经授权获取、查看或分析他人聊天记录,或侵犯他人隐私权。 24 | - 用户应采取适当措施保护通过本工具获取和处理的聊天数据安全,包括但不限于加密存储、限制访问权限、定期删除不必要数据等。 25 | - 用户应确保其处理的聊天数据符合相关数据保护法规,包括但不限于获得必要的同意、保障数据主体权利、遵守数据最小化原则等。 26 | 27 | ## 4. 使用限制 28 | 29 | - 本项目仅允许在合法授权情况下对聊天数据库进行备份与查看。 30 | - 未经明确授权,严禁将本项目用于访问、查看、分析或处理任何第三方聊天数据。 31 | - 使用第三方 LLM 服务时,用户应遵守相关服务提供商的服务条款和使用政策。 32 | - 用户不得规避本项目中的任何技术限制,或尝试反向工程、反编译或反汇编本项目,除非适用法律明确允许此类活动。 33 | 34 | ## 5. 技术风险声明 35 | 36 | ⚠️ **使用本项目存在以下技术风险,用户应充分了解并自行承担:** 37 | 38 | - 本工具需要访问聊天软件的数据库文件,可能因聊天软件版本更新导致功能失效或数据不兼容。 39 | - 在 macOS 系统上使用时,需要临时关闭 SIP 安全机制,这可能降低系统安全性,用户应了解相关风险并自行决定是否使用。 40 | - 本项目可能存在未知的技术缺陷或安全漏洞,可能导致数据损坏、丢失或泄露。 41 | - 使用本项目处理大量数据可能导致系统性能下降或资源占用过高。 42 | - 第三方依赖库或 API 的变更可能影响本项目的功能或安全性。 43 | 44 | ## 6. 禁止非法用途 45 | 46 | 严禁将本项目用于以下用途: 47 | 48 | - 从事任何形式的非法活动,包括但不限于未授权系统测试、网络渗透或其他违反法律法规的行为。 49 | - 监控、窃取或未经授权获取他人聊天记录或个人信息。 50 | - 将获取的数据用于骚扰、诈骗、敲诈、威胁或其他侵害他人合法权益的行为。 51 | - 规避任何安全措施或访问控制机制。 52 | - 传播虚假信息、仇恨言论或违反公序良俗的内容。 53 | - 侵犯任何第三方的知识产权、隐私权或其他合法权益。 54 | 55 | **违反上述规定的,用户应自行承担全部法律责任,并赔偿因此给开发者或第三方造成的全部损失。** 56 | 57 | ## 7. 第三方服务集成 58 | 59 | - 用户将聊天数据与第三方 LLM 服务(如 OpenAI、Claude 等)结合使用时,应仔细阅读并遵守这些服务的使用条款、隐私政策和数据处理协议。 60 | - 用户应了解,向第三方服务传输数据可能导致数据离开用户控制范围,并受第三方服务条款约束。 61 | - 本项目开发者不对第三方服务的可用性、安全性、准确性或数据处理行为负责,用户应自行评估相关风险。 62 | - 用户应确保其向第三方服务传输数据的行为符合适用的数据保护法规和第三方服务条款。 63 | 64 | ## 8. 责任限制 65 | 66 | **在法律允许的最大范围内:** 67 | 68 | - 本项目按"原样"和"可用"状态提供,不对功能的适用性、可靠性、准确性、完整性或及时性做任何明示或暗示的保证。 69 | - 开发者明确否认对适销性、特定用途适用性、不侵权以及任何其他明示或暗示的保证。 70 | - 本项目开发者和贡献者不对用户使用本工具的行为及后果承担任何法律责任。 71 | - 对于因使用本工具而可能导致的任何直接、间接、附带、特殊、惩罚性或后果性损失,包括但不限于数据丢失、业务中断、隐私泄露、声誉损害、利润损失、法律纠纷等,本项目开发者概不负责,即使开发者已被告知此类损失的可能性。 72 | - 在任何情况下,开发者对用户的全部责任累计不超过用户为获取本软件实际支付的金额(如为免费获取则为零)。 73 | 74 | ## 9. 知识产权声明 75 | 76 | - 本项目基于 Apache-2.0 许可证开源,用户在使用、修改和分发时应严格遵守该许可证的所有条款。 77 | - 本项目的名称"Chatlog"、相关标识及商标权(如有)归开发者所有,未经明确授权,用户不得以任何方式使用这些标识进行商业活动。 78 | - 根据 Apache-2.0 许可证,用户可自由使用、修改和分发本项目代码,但须遵守许可证规定的归属声明等要求。 79 | - 用户对其修改版本自行承担全部责任,且不得以原项目名义发布,必须明确标明其为修改版本并与原项目区分。 80 | - 用户不得移除或更改本项目中的版权声明、商标或其他所有权声明。 81 | 82 | ## 10. 数据处理合规性 83 | 84 | - 用户在使用本项目处理个人数据时,应遵守适用的数据保护法规,包括但不限于《中华人民共和国个人信息保护法》、《通用数据保护条例》(GDPR)等。 85 | - 用户应确保其具有处理相关数据的合法依据,如获得数据主体的明确同意。 86 | - 用户应实施适当的技术和组织措施,确保数据安全,防止未授权访问、意外丢失或泄露。 87 | - 在跨境传输数据时,用户应确保符合相关法律对数据出境的要求。 88 | - 用户应尊重数据主体权利,包括访问权、更正权、删除权等。 89 | 90 | ## 11. 免责声明接受 91 | 92 | 下载、安装、使用本项目,表示用户已阅读、理解并同意遵守本免责声明的所有条款。如不同意,请立即停止使用本工具并删除相关代码和程序。 93 | 94 | **用户确认:** 95 | - 已完整阅读并理解本免责声明的全部内容 96 | - 自愿接受本免责声明的全部条款 97 | - 具有完全民事行为能力,能够理解并承担使用本项目的风险和责任 98 | - 将遵守本免责声明中规定的所有义务和限制 99 | 100 | ## 12. 免责声明修改与通知 101 | 102 | - 本免责声明可能根据项目发展和法律法规变化进行修改和调整,修改后的声明将在项目官方仓库页面公布。 103 | - 开发者没有义务个别通知用户免责声明的变更,用户应定期查阅最新版本。 104 | - 重大变更将通过项目仓库的 Release Notes 或 README 文件更新进行通知。 105 | - 在免责声明更新后继续使用本项目,即视为接受修改后的条款。 106 | 107 | ## 13. 法律适用与管辖 108 | 109 | - 本免责声明受中华人民共和国法律管辖,并按其解释。 110 | - 任何与本免责声明有关的争议,应首先通过友好协商解决;协商不成的,提交至本项目开发者所在地有管辖权的人民法院诉讼解决。 111 | - 对于中国境外用户,如本免责声明与用户所在地强制性法律规定冲突,应以不违反该强制性规定的方式解释和适用本声明,但本声明的其余部分仍然有效。 112 | 113 | ## 14. 可分割性 114 | 115 | 如本免责声明中的任何条款被有管辖权的法院或其他权威机构认定为无效、不合法或不可执行,不影响其余条款的有效性和可执行性。无效条款应被视为从本声明中分割,并在法律允许的最大范围内由最接近原条款意图的有效条款替代。 116 | 117 | ## 15. 完整协议 118 | 119 | 本免责声明构成用户与开发者之间关于本项目使用的完整协议,取代先前或同时期关于本项目的所有口头或书面协议、提议和陈述。本声明的任何豁免、修改或补充均应以书面形式作出并经开发者签署方为有效。 120 | 121 | 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINARY_NAME := chatlog 2 | GO := go 3 | ifeq ($(VERSION),) 4 | VERSION := $(shell git describe --tags --always --dirty="-dev") 5 | endif 6 | LDFLAGS := -ldflags '-X "github.com/sjzar/chatlog/pkg/version.Version=$(VERSION)" -w -s' 7 | 8 | PLATFORMS := \ 9 | darwin/amd64 \ 10 | darwin/arm64 \ 11 | windows/amd64 \ 12 | windows/arm64 13 | 14 | UPX_PLATFORMS := \ 15 | darwin/amd64 \ 16 | windows/386 \ 17 | windows/amd64 18 | 19 | .PHONY: all clean lint tidy test build crossbuild upx 20 | 21 | all: clean lint tidy test build 22 | 23 | clean: 24 | @echo "🧹 Cleaning..." 25 | @rm -rf bin/ 26 | 27 | lint: 28 | @echo "🕵️‍♂️ Running linters..." 29 | golangci-lint run ./... 30 | 31 | tidy: 32 | @echo "🧼 Tidying up dependencies..." 33 | $(GO) mod tidy 34 | 35 | test: 36 | @echo "🧪 Running tests..." 37 | $(GO) test ./... -cover 38 | 39 | build: 40 | @echo "🔨 Building for current platform..." 41 | CGO_ENABLED=1 $(GO) build -trimpath $(LDFLAGS) -o bin/$(BINARY_NAME) main.go 42 | 43 | crossbuild: clean 44 | @echo "🌍 Building for multiple platforms..." 45 | for platform in $(PLATFORMS); do \ 46 | os=$$(echo $$platform | cut -d/ -f1); \ 47 | arch=$$(echo $$platform | cut -d/ -f2); \ 48 | float=$$(echo $$platform | cut -d/ -f3); \ 49 | output_name=bin/chatlog_$${os}_$${arch}; \ 50 | [ "$$float" != "" ] && output_name=$$output_name_$$float; \ 51 | echo "🔨 Building for $$os/$$arch..."; \ 52 | echo "🔨 Building for $$output_name..."; \ 53 | GOOS=$$os GOARCH=$$arch CGO_ENABLED=1 GOARM=$$float $(GO) build -trimpath $(LDFLAGS) -o $$output_name main.go ; \ 54 | if [ "$(ENABLE_UPX)" = "1" ] && echo "$(UPX_PLATFORMS)" | grep -q "$$os/$$arch"; then \ 55 | echo "⚙️ Compressing binary $$output_name..." && upx --best $$output_name; \ 56 | fi; \ 57 | done -------------------------------------------------------------------------------- /cmd/chatlog/cmd_decrypt.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/sjzar/chatlog/internal/chatlog" 8 | 9 | "github.com/rs/zerolog/log" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | rootCmd.AddCommand(decryptCmd) 15 | decryptCmd.Flags().StringVarP(&dataDir, "data-dir", "d", "", "data dir") 16 | decryptCmd.Flags().StringVarP(&workDir, "work-dir", "w", "", "work dir") 17 | decryptCmd.Flags().StringVarP(&key, "key", "k", "", "key") 18 | decryptCmd.Flags().StringVarP(&decryptPlatform, "platform", "p", runtime.GOOS, "platform") 19 | decryptCmd.Flags().IntVarP(&decryptVer, "version", "v", 3, "version") 20 | } 21 | 22 | var ( 23 | dataDir string 24 | workDir string 25 | key string 26 | decryptPlatform string 27 | decryptVer int 28 | ) 29 | 30 | var decryptCmd = &cobra.Command{ 31 | Use: "decrypt", 32 | Short: "decrypt", 33 | Run: func(cmd *cobra.Command, args []string) { 34 | m, err := chatlog.New("") 35 | if err != nil { 36 | log.Err(err).Msg("failed to create chatlog instance") 37 | return 38 | } 39 | if err := m.CommandDecrypt(dataDir, workDir, key, decryptPlatform, decryptVer); err != nil { 40 | log.Err(err).Msg("failed to decrypt") 41 | return 42 | } 43 | fmt.Println("decrypt success") 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /cmd/chatlog/cmd_dumpmemory.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "runtime" 10 | "time" 11 | 12 | "github.com/rs/zerolog/log" 13 | "github.com/spf13/cobra" 14 | 15 | "github.com/sjzar/chatlog/internal/wechat" 16 | "github.com/sjzar/chatlog/internal/wechat/key/darwin/glance" 17 | ) 18 | 19 | func init() { 20 | rootCmd.AddCommand(dumpmemoryCmd) 21 | } 22 | 23 | var dumpmemoryCmd = &cobra.Command{ 24 | Use: "dumpmemory", 25 | Short: "dump memory", 26 | Run: func(cmd *cobra.Command, args []string) { 27 | if runtime.GOOS != "darwin" { 28 | log.Info().Msg("dump memory only support macOS") 29 | } 30 | 31 | session := time.Now().Format("20060102150405") 32 | 33 | dir, err := os.Getwd() 34 | if err != nil { 35 | log.Fatal().Err(err).Msg("get current directory failed") 36 | return 37 | } 38 | log.Info().Msgf("current directory: %s", dir) 39 | 40 | // step 1. check pid 41 | if err = wechat.Load(); err != nil { 42 | log.Fatal().Err(err).Msg("load wechat failed") 43 | return 44 | } 45 | accounts := wechat.GetAccounts() 46 | if len(accounts) == 0 { 47 | log.Fatal().Msg("no wechat account found") 48 | return 49 | } 50 | 51 | log.Info().Msgf("found %d wechat account", len(accounts)) 52 | for i, a := range accounts { 53 | log.Info().Msgf("%d. %s %d %s", i, a.FullVersion, a.PID, a.DataDir) 54 | } 55 | 56 | // step 2. dump memory 57 | account := accounts[0] 58 | file := fmt.Sprintf("wechat_%s_%d_%s.bin", account.FullVersion, account.PID, session) 59 | path := filepath.Join(dir, file) 60 | log.Info().Msgf("dumping memory to %s", path) 61 | 62 | g := glance.NewGlance(account.PID) 63 | b, err := g.Read() 64 | if err != nil { 65 | log.Fatal().Err(err).Msg("read memory failed") 66 | return 67 | } 68 | 69 | if err = os.WriteFile(path, b, 0644); err != nil { 70 | log.Fatal().Err(err).Msg("write memory failed") 71 | return 72 | } 73 | 74 | log.Info().Msg("dump memory success") 75 | 76 | // step 3. copy encrypted database file 77 | dbFile := "db_storage/session/session.db" 78 | if account.Version == 3 { 79 | dbFile = "Session/session_new.db" 80 | } 81 | from := filepath.Join(account.DataDir, dbFile) 82 | to := filepath.Join(dir, fmt.Sprintf("wechat_%s_%d_session.db", account.FullVersion, account.PID)) 83 | 84 | log.Info().Msgf("copying %s to %s", from, to) 85 | b, err = os.ReadFile(from) 86 | if err != nil { 87 | log.Fatal().Err(err).Msg("read session.db failed") 88 | return 89 | } 90 | if err = os.WriteFile(to, b, 0644); err != nil { 91 | log.Fatal().Err(err).Msg("write session.db failed") 92 | return 93 | } 94 | log.Info().Msg("copy session.db success") 95 | 96 | // step 4. package 97 | zipFile := fmt.Sprintf("wechat_%s_%d_%s.zip", account.FullVersion, account.PID, session) 98 | zipPath := filepath.Join(dir, zipFile) 99 | log.Info().Msgf("packaging to %s", zipPath) 100 | 101 | zf, err := os.Create(zipPath) 102 | if err != nil { 103 | log.Fatal().Err(err).Msg("create zip file failed") 104 | return 105 | } 106 | defer zf.Close() 107 | 108 | zw := zip.NewWriter(zf) 109 | 110 | for _, file := range []string{file, to} { 111 | f, err := os.Open(file) 112 | if err != nil { 113 | log.Fatal().Err(err).Msg("open file failed") 114 | return 115 | } 116 | defer f.Close() 117 | info, err := f.Stat() 118 | if err != nil { 119 | log.Fatal().Err(err).Msg("get file info failed") 120 | return 121 | } 122 | header, err := zip.FileInfoHeader(info) 123 | if err != nil { 124 | log.Fatal().Err(err).Msg("create zip file info header failed") 125 | return 126 | } 127 | header.Name = filepath.Base(file) 128 | header.Method = zip.Deflate 129 | writer, err := zw.CreateHeader(header) 130 | if err != nil { 131 | log.Fatal().Err(err).Msg("create zip file header failed") 132 | return 133 | } 134 | if _, err = io.Copy(writer, f); err != nil { 135 | log.Fatal().Err(err).Msg("copy file to zip failed") 136 | return 137 | } 138 | } 139 | if err = zw.Close(); err != nil { 140 | log.Fatal().Err(err).Msg("close zip writer failed") 141 | return 142 | } 143 | 144 | log.Info().Msgf("package success, please send %s to developer", zipPath) 145 | }, 146 | } 147 | -------------------------------------------------------------------------------- /cmd/chatlog/cmd_key.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sjzar/chatlog/internal/chatlog" 7 | 8 | "github.com/rs/zerolog/log" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | rootCmd.AddCommand(keyCmd) 14 | keyCmd.Flags().IntVarP(&pid, "pid", "p", 0, "pid") 15 | } 16 | 17 | var pid int 18 | var keyCmd = &cobra.Command{ 19 | Use: "key", 20 | Short: "key", 21 | Run: func(cmd *cobra.Command, args []string) { 22 | m, err := chatlog.New("") 23 | if err != nil { 24 | log.Err(err).Msg("failed to create chatlog instance") 25 | return 26 | } 27 | ret, err := m.CommandKey(pid) 28 | if err != nil { 29 | log.Err(err).Msg("failed to get key") 30 | return 31 | } 32 | fmt.Println(ret) 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /cmd/chatlog/cmd_server.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/sjzar/chatlog/internal/chatlog" 7 | 8 | "github.com/rs/zerolog/log" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | rootCmd.AddCommand(serverCmd) 14 | serverCmd.Flags().StringVarP(&serverAddr, "addr", "a", "127.0.0.1:5030", "server address") 15 | serverCmd.Flags().StringVarP(&serverDataDir, "data-dir", "d", "", "data dir") 16 | serverCmd.Flags().StringVarP(&serverWorkDir, "work-dir", "w", "", "work dir") 17 | serverCmd.Flags().StringVarP(&serverPlatform, "platform", "p", runtime.GOOS, "platform") 18 | serverCmd.Flags().IntVarP(&serverVer, "version", "v", 3, "version") 19 | } 20 | 21 | var ( 22 | serverAddr string 23 | serverDataDir string 24 | serverWorkDir string 25 | serverPlatform string 26 | serverVer int 27 | ) 28 | 29 | var serverCmd = &cobra.Command{ 30 | Use: "server", 31 | Short: "Start HTTP server", 32 | Run: func(cmd *cobra.Command, args []string) { 33 | m, err := chatlog.New("") 34 | if err != nil { 35 | log.Err(err).Msg("failed to create chatlog instance") 36 | return 37 | } 38 | if err := m.CommandHTTPServer(serverAddr, serverDataDir, serverWorkDir, serverPlatform, serverVer); err != nil { 39 | log.Err(err).Msg("failed to start server") 40 | return 41 | } 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /cmd/chatlog/cmd_version.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sjzar/chatlog/pkg/version" 7 | 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | rootCmd.AddCommand(versionCmd) 13 | versionCmd.Flags().BoolVarP(&versionM, "module", "m", false, "module version information") 14 | } 15 | 16 | var versionM bool 17 | var versionCmd = &cobra.Command{ 18 | Use: "version [-m]", 19 | Short: "Show the version of chatlog", 20 | Run: func(cmd *cobra.Command, args []string) { 21 | if versionM { 22 | fmt.Println(version.GetMore(true)) 23 | } else { 24 | fmt.Printf("chatlog %s\n", version.GetMore(false)) 25 | } 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /cmd/chatlog/log.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | 9 | "github.com/sjzar/chatlog/pkg/util" 10 | 11 | "github.com/rs/zerolog" 12 | "github.com/rs/zerolog/log" 13 | "github.com/sirupsen/logrus" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var Debug bool 18 | 19 | func initLog(cmd *cobra.Command, args []string) { 20 | zerolog.SetGlobalLevel(zerolog.InfoLevel) 21 | 22 | if Debug { 23 | zerolog.SetGlobalLevel(zerolog.DebugLevel) 24 | } 25 | 26 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}) 27 | } 28 | 29 | func initTuiLog(cmd *cobra.Command, args []string) { 30 | logOutput := io.Discard 31 | 32 | debug, _ := cmd.Flags().GetBool("debug") 33 | if debug { 34 | logpath := util.DefaultWorkDir("") 35 | util.PrepareDir(logpath) 36 | logFD, err := os.OpenFile(filepath.Join(logpath, "chatlog.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm) 37 | if err != nil { 38 | panic(err) 39 | } 40 | logOutput = logFD 41 | } 42 | 43 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: logOutput, NoColor: true, TimeFormat: time.RFC3339}) 44 | logrus.SetOutput(logOutput) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/chatlog/root.go: -------------------------------------------------------------------------------- 1 | package chatlog 2 | 3 | import ( 4 | "github.com/sjzar/chatlog/internal/chatlog" 5 | 6 | "github.com/rs/zerolog/log" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | // windows only 12 | cobra.MousetrapHelpText = "" 13 | 14 | rootCmd.PersistentFlags().BoolVar(&Debug, "debug", false, "debug") 15 | rootCmd.PersistentPreRun = initLog 16 | } 17 | 18 | func Execute() { 19 | if err := rootCmd.Execute(); err != nil { 20 | log.Err(err).Msg("command execution failed") 21 | } 22 | } 23 | 24 | var rootCmd = &cobra.Command{ 25 | Use: "chatlog", 26 | Short: "chatlog", 27 | Long: `chatlog`, 28 | Example: `chatlog`, 29 | Args: cobra.MinimumNArgs(0), 30 | CompletionOptions: cobra.CompletionOptions{ 31 | HiddenDefaultCmd: true, 32 | }, 33 | PreRun: initTuiLog, 34 | Run: Root, 35 | } 36 | 37 | func Root(cmd *cobra.Command, args []string) { 38 | 39 | m, err := chatlog.New("") 40 | if err != nil { 41 | log.Err(err).Msg("failed to create chatlog instance") 42 | return 43 | } 44 | 45 | if err := m.Run(); err != nil { 46 | log.Err(err).Msg("failed to run chatlog instance") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs/mcp.md: -------------------------------------------------------------------------------- 1 | # MCP 集成指南 2 | 3 | ## 目录 4 | - [MCP 集成指南](#mcp-集成指南) 5 | - [目录](#目录) 6 | - [前期准备](#前期准备) 7 | - [mcp-proxy](#mcp-proxy) 8 | - [ChatWise](#chatwise) 9 | - [Cherry Studio](#cherry-studio) 10 | - [Claude Desktop](#claude-desktop) 11 | - [Monica Code](#monica-code) 12 | 13 | 14 | ## 前期准备 15 | 16 | 运行 `chatlog`,完成数据解密并开启 HTTP 服务 17 | 18 | ### mcp-proxy 19 | 如果遇到不支持 `SSE` 的客户端,可以尝试使用 `mcp-proxy` 将 `stdio` 的请求转换为 `SSE`。 20 | 21 | 项目地址:https://github.com/sparfenyuk/mcp-proxy 22 | 23 | 安装方式: 24 | ```shell 25 | # 使用 uv 工具安装,也可参考项目文档的其他安装方式 26 | uv tool install mcp-proxy 27 | 28 | # 查询 mcp-proxy 的路径,后续可直接使用该路径 29 | which mcp-proxy 30 | /Users/sarv/.local/bin/mcp-proxy 31 | ``` 32 | 33 | ## ChatWise 34 | 35 | - 官网:https://chatwise.app/ 36 | - 使用方式:MCP SSE 37 | - 注意事项:使用 ChatWise 的 MCP 功能需要 Pro 权限 38 | 39 | 1. 在 `设置 - 工具` 下新建 `SSE 请求` 工具 40 | 41 | ![chatwise-1](https://github.com/user-attachments/assets/87e40f39-9fbc-4ff1-954a-d95548cde4c2) 42 | 43 | 1. 在 URL 中填写 `http://127.0.0.1:5030/sse`,并勾选 `自动执行工具`,点击 `查看工具` 即可检查连接 `chatlog` 是否正常 44 | 45 | ![chatwise-2](https://github.com/user-attachments/assets/8f98ef18-8e6c-40e6-ae78-8cd13e411c36) 46 | 47 | 3. 返回主页,选择支持 MCP 调用的模型,打开 `chatlog` 工具选项 48 | 49 | ![chatwise-3](https://github.com/user-attachments/assets/ea2aa178-5439-492b-a92f-4f4fc08828e7) 50 | 51 | 4. 测试功能是否正常 52 | 53 | ![chatwise-4](https://github.com/user-attachments/assets/8f82cb53-8372-40ee-a299-c02d3399403a) 54 | 55 | ## Cherry Studio 56 | 57 | - 官网:https://cherry-ai.com/ 58 | - 使用方式:MCP SSE 59 | 60 | 1. 在 `设置 - MCP 服务器` 下点击 `添加服务器`,输入名称为 `chatlog`,选择类型为 `服务器发送事件(sse)`,填写 URL 为 `http://127.0.0.1:5030/sse`,点击 `保存`。(注意:点击保存前不要先点击左侧的开启按钮) 61 | 62 | ![cherry-1](https://github.com/user-attachments/assets/93fc8b0a-9d95-499e-ab6c-e22b0c96fd6a) 63 | 64 | 2. 选择支持 MCP 调用的模型,打开 `chatlog` 工具选项 65 | 66 | ![cherry-2](https://github.com/user-attachments/assets/4e5bf752-2eab-4e7c-b73b-1b759d4a5f29) 67 | 68 | 3. 测试功能是否正常 69 | 70 | ![cherry-3](https://github.com/user-attachments/assets/c58a019f-fd5f-4fa3-830a-e81a60f2aa6f) 71 | 72 | ## Claude Desktop 73 | 74 | - 官网:https://claude.ai/download 75 | - 使用方式:mcp-proxy 76 | - 参考资料:https://modelcontextprotocol.io/quickstart/user#2-add-the-filesystem-mcp-server 77 | 78 | 1. 请先参考 [mcp-proxy](#mcp-proxy) 安装 `mcp-proxy` 79 | 80 | 2. 进入 Claude Desktop `Settings - Developer`,点击 `Edit Config` 按钮,这样会创建一个 `claude_desktop_config.json` 配置文件,并引导你编辑该文件 81 | 82 | 3. 编辑 `claude_desktop_config.json` 文件,配置名称为 `chatlog`,command 为 `mcp-proxy` 的路径,args 为 `http://127.0.0.1:5030/sse`,如下所示: 83 | 84 | ```json 85 | { 86 | "mcpServers": { 87 | "chatlog": { 88 | "command": "/Users/sarv/.local/bin/mcp-proxy", 89 | "args": [ 90 | "http://localhost:5030/sse" 91 | ] 92 | } 93 | }, 94 | "globalShortcut": "" 95 | } 96 | ``` 97 | 98 | 4. 保存 `claude_desktop_config.json` 文件,重启 Claude Desktop,可以看到 `chatlog` 已经添加成功 99 | 100 | ![claude-1](https://github.com/user-attachments/assets/f4e872cc-e6c1-4e24-97da-266466949cdf) 101 | 102 | 5. 测试功能是否正常 103 | 104 | ![claude-2](https://github.com/user-attachments/assets/832bb4d2-3639-4cbc-8b17-f4b812ea3637) 105 | 106 | 107 | ## Monica Code 108 | 109 | - 官网:https://monica.im/en/code 110 | - 使用方式:mcp-proxy 111 | - 参考资料:https://github.com/Monica-IM/Monica-Code/blob/main/Reference/config.md#modelcontextprotocolserver 112 | 113 | 1. 请先参考 [mcp-proxy](#mcp-proxy) 安装 `mcp-proxy` 114 | 115 | 2. 在 vscode 插件文件夹(`~/.vscode/extensions`)下找到 Monica Code 的目录,编辑 `config_schema.json` 文件。将 `experimental - modelContextProtocolServer` 中 `transport` 设置为如下内容: 116 | 117 | ```json 118 | { 119 | "experimental": { 120 | "type": "object", 121 | "title": "Experimental", 122 | "description": "Experimental properties are subject to change.", 123 | "properties": { 124 | "modelContextProtocolServer": { 125 | "type": "object", 126 | "properties": { 127 | "transport": { 128 | "type": "stdio", 129 | "command": "/Users/sarv/.local/bin/mcp-proxy", 130 | "args": [ 131 | "http://localhost:5030/sse" 132 | ] 133 | } 134 | }, 135 | "required": [ 136 | "transport" 137 | ] 138 | } 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | 3. 重启 vscode,可以看到 `chatlog` 已经添加成功 145 | 146 | ![monica-1](https://github.com/user-attachments/assets/8d0a96f2-ed05-48aa-a99a-06648ae1c500) 147 | 148 | 4. 测试功能是否正常 149 | 150 | ![monica-2](https://github.com/user-attachments/assets/054e0a30-428a-48a6-9f31-d2596fb8f743) 151 | 152 | -------------------------------------------------------------------------------- /docs/prompt.md: -------------------------------------------------------------------------------- 1 | # Prompt 指南 2 | 3 | ## 概述 4 | 优秀的 `prompt` 可以极大的提高 `chatlog` 使用体验,收集了部分群友分享的 `prompt`,供大家参考。 5 | 在处理聊天记录时,尽量选择上下文长度足够的 LLM,例如 `Gemini 2.5 Pro`、`Claude 3.5 Sonnet` 等。 6 | 欢迎大家在 [Discussions](https://github.com/sjzar/chatlog/discussions/47) 中分享自己的使用方式,共同进步。 7 | 8 | 9 | ## 群聊总结 10 | 作者:@eyaeya 11 | 12 | ```md 13 | 你是一个中文的群聊总结的助手,你可以为一个微信的群聊记录,提取并总结每个时间段大家在重点讨论的话题内容。 14 | 15 | 请帮我将 "" 在