├── .github └── workflows │ ├── build-linux-windows.yml │ └── build-macos.yml ├── .gitignore ├── .goreleaser.yml ├── FyneApp.toml ├── LICENSE ├── README.md ├── README_EN.md ├── active.en-US.toml ├── active.zh-CN.toml ├── args.go ├── assets ├── domains.json ├── index.html ├── public │ ├── docs │ │ ├── client-custom.png │ │ ├── client-select.png │ │ ├── client-start.png │ │ ├── client.png │ │ └── server.png │ ├── logo.png │ └── style.min.css └── zcool-cryyt.ttf ├── conf.go ├── consts.go ├── fetch_hosts.go ├── go.mod ├── go.sum ├── gui.go ├── gui_theme.go ├── i18n.go ├── log.go ├── main.go ├── no_gui.go ├── ticker.go ├── use.sh └── util.go /.github/workflows/build-linux-windows.yml: -------------------------------------------------------------------------------- 1 | name: Build for Linux & Windows 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set env 18 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 19 | 20 | - name: Install Docker 21 | run: curl -fsSL https://get.docker.com | bash -s docker 22 | 23 | - name: Setup Go 24 | uses: actions/setup-go@v2 25 | with: 26 | go-version: 1.20.2 27 | 28 | - name: Go tidy 29 | run: go mod tidy 30 | 31 | - name: Install fyne-cross 32 | run: go get github.com/fyne-io/fyne-cross && go install github.com/fyne-io/fyne-cross 33 | 34 | - name: Package for Windows 35 | run: fyne-cross windows -arch=* 36 | 37 | - name: Package for Linux 38 | run: fyne-cross linux -arch=* 39 | 40 | - name: Create pkg dist dir 41 | run: mkdir pkg-dist 42 | 43 | - name: Move Windows-amd64 44 | run: mv fyne-cross/dist/windows-amd64/Fetch-Github-Hosts.exe.zip pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_windows_amd64.zip 45 | 46 | - name: Move Windows-386 47 | run: mv fyne-cross/dist/windows-386/Fetch-Github-Hosts.exe.zip pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_windows_386.zip 48 | 49 | - name: Move Linux-386 50 | run: mv fyne-cross/dist/linux-386/Fetch-Github-Hosts.tar.xz pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_386.tar.xz 51 | 52 | - name: Move Linux-amd64 53 | run: mv fyne-cross/dist/linux-amd64/Fetch-Github-Hosts.tar.xz pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_amd64.tar.xz 54 | 55 | - name: Move Linux-arm 56 | run: mv fyne-cross/dist/linux-arm/Fetch-Github-Hosts.tar.xz pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_arm.tar.xz 57 | 58 | - name: Move Linux-arm64 59 | run: mv fyne-cross/dist/linux-arm64/Fetch-Github-Hosts.tar.xz pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_arm64.tar.xz 60 | 61 | - uses: actions/upload-artifact@v3 62 | with: 63 | name: build-result 64 | path: | 65 | pkg-dist 66 | 67 | - name: Release 68 | uses: softprops/action-gh-release@v1 69 | if: startsWith(github.ref, 'refs/tags/') 70 | with: 71 | draft: true 72 | append_body: true 73 | files: | 74 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_windows_amd64.zip 75 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_windows_386.zip 76 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_386.tar.xz 77 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_amd64.tar.xz 78 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_arm.tar.xz 79 | pkg-dist/fetch-github-hosts_${{ env.RELEASE_VERSION }}_linux_arm64.tar.xz 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/build-macos.yml: -------------------------------------------------------------------------------- 1 | name: Build for MacOS 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build: 10 | runs-on: macos-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set env 18 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: 1.20.2 24 | 25 | - name: Go tidy 26 | run: go mod tidy 27 | 28 | - name: Install fyne 29 | run: go get fyne.io/fyne/v2/cmd/fyne && go install fyne.io/fyne/v2/cmd/fyne 30 | 31 | - name: Package for MacOS 32 | run: fyne package --release -os darwin 33 | 34 | - name: Compress 35 | uses: a7ul/tar-action@v1.1.0 36 | id: compress 37 | with: 38 | command: c 39 | files: | 40 | LICENSE 41 | README.md 42 | Fetch-Github-Hosts.app 43 | outPath: fetch-github-hosts_${{ env.RELEASE_VERSION }}_macOS_amd64.tar.gz 44 | 45 | - name: Release 46 | uses: softprops/action-gh-release@v1 47 | if: startsWith(github.ref, 'refs/tags/') 48 | with: 49 | draft: true 50 | append_body: true 51 | files: fetch-github-hosts_${{ env.RELEASE_VERSION }}_macOS_amd64.tar.gz 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | hosts.txt 3 | hosts.json 4 | index.php 5 | /index.html 6 | output 7 | conf.yaml 8 | dist 9 | *.log 10 | translate.*.toml -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - 3 | goos: 4 | - darwin 5 | - linux 6 | - windows 7 | goarch: 8 | - 386 9 | - amd64 10 | - arm64 11 | - arm 12 | env: 13 | - CGO_ENABLED=0 -------------------------------------------------------------------------------- /FyneApp.toml: -------------------------------------------------------------------------------- 1 | Website = "https://github.com/Licoy/fetch-github-hosts" 2 | 3 | [Details] 4 | Icon = "assets/public/logo.png" 5 | Name = "Fetch-Github-Hosts" 6 | ID = "com.github.licoy.fetch-github-hosts" 7 | Version = "2.8" 8 | Build = 58 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README_EN.md) 2 | 3 |
4 |

Fetch GitHub Hosts

5 | 6 | ![LOGO](assets/public/logo.png) 7 | 8 | `fetch-github-hosts` 是主要为解决研究及学习人员访问 `Github` 过慢或其他问题而提供的 `Github Hosts` 同步工具 9 | 10 | [![Release](https://img.shields.io/github/v/release/Licoy/fetch-github-hosts.svg?logo=git)](https://github.com/Licoy/fetch-github-hosts) 11 | [![Build Linux & Windows](https://github.com/Licoy/fetch-github-hosts/workflows/Build%20for%20Linux%20&%20Windows/badge.svg)](https://github.com/Licoy/fetch-github-hosts) 12 | [![Build MacOS](https://github.com/Licoy/fetch-github-hosts/workflows/Build%20for%20MacOS/badge.svg)](https://github.com/Licoy/fetch-github-hosts) 13 | 14 |
15 | 16 | ## no-gui 构建方法 17 | 18 | ```bash 19 | go build -tags="no_gui" 20 | ``` 21 | 22 | ## 原理 23 | 24 | 此项目是通过部署此项目本身的服务器来获取 `github.com` 的 `hosts`,而不是通过第三方ip地址接口来进行获取,例如 `ipaddress.com` 等。 25 | 26 | ## 使用方法 27 | ### 图形化界面 28 | 到 [Releases](https://github.com/Licoy/fetch-github-hosts/releases) 中下载您的系统版本(目前支持`Windows`/`Linux`/`MacOS` 29 | ) 30 | 31 | 下载完成解压`tar.gz`压缩包,运行对应平台的执行文件即可运行( ⚠️ 注意:Linux下需要用`sudo`进行启动,Windows和MacOS会自动进行提权操作。) 32 | 33 | #### 客户端模式 34 | ![client](assets/public/docs/client.png) 35 | 36 | #### 客户端启动 37 | ![client-start](assets/public/docs/client-start.png) 38 | 39 | #### 客户端hosts源选择 40 | ![client-select](assets/public/docs/client-select.png) 41 | 42 | #### 客户端hosts源自定义 43 | ![client-custom](assets/public/docs/client-custom.png) 44 | 45 | #### 服务端模式 46 | ![server](assets/public/docs/server.png) 47 | 48 | ### 命令行终端 49 | 50 | 到 [Releases](https://github.com/Licoy/fetch-github-hosts/releases) 中下载您的系统版本(目前支持`Windows`/`Linux`/`MacOS` 51 | ) 52 | 53 | #### 参数 54 | 55 | | 参数名 | 缩写 | 默认值 | 必填 | 描述 | 56 | |------------|-----|--------------------------------------|-----|------------------------------------| 57 | | `mode` | `m` | 无 | 是 | 启动模式 `server(服务端)` / `client(客户端)` | 58 | | `interval` | `i` | 60 | 否 | 获取记录值间隔(分钟) | 59 | | `port` | `p` | 9898 | 否 | 服务模式监听端口以访问HTTP服务 | 60 | | `url` | `u` | `https://hosts.gitcdn.top/hosts.txt` | 否 | 客户端模式远程hosts获取链接 | 61 | | `lang` | `l` | `zh-CN` | 否 | 界面语言 | 62 | 63 | #### 启动客户端: 64 | 65 | > 注意: 66 | > 67 | > Linux下需要使用`sudo`运行; 68 | > 69 | > Windows和MacOS会自动进行提权操作。 70 | 71 | - 直接运行 72 | 73 | ```bash 74 | # Linux/Macos 75 | sudo fetch-github-hosts -m=client 76 | 77 | # Windows 78 | fetch-github-hosts.exe -m=client 79 | ``` 80 | 81 | - 自定义获取时间间隔 82 | 83 | ```bash 84 | # Linux/Macos(10分钟获取一次) 85 | sudo fetch-github-hosts -i=10 86 | 87 | # Windows(10分钟获取一次) 88 | fetch-github-hosts.exe -i=10 89 | ``` 90 | 91 | - 自定义获取链接 92 | 93 | ```bash 94 | # Linux/Macos 95 | sudo fetch-github-hosts -u=http://127.0.0.1:9898/hosts.json 96 | 97 | # Windows 98 | fetch-github-hosts.exe -u=http://127.0.0.1:9898/hosts.json 99 | ``` 100 | 101 | #### 启动服务端: 102 | 103 | - 直接运行 104 | 105 | ```bash 106 | # Linux/Macos 107 | fetch-github-hosts -m=server 108 | 109 | # Windows 110 | fetch-github-hosts.exe -m=server 111 | ``` 112 | 113 | - 自定义监听端口 114 | 115 | ```bash 116 | # Linux/Macos 117 | fetch-github-hosts -m=server -p=6666 118 | 119 | # Windows 120 | fetch-github-hosts.exe -m=server -p=6666 121 | ``` 122 | 123 | ### 手动 124 | 125 | #### 添加hosts 126 | 127 | 访问 [https://hosts.gitcdn.top/hosts.txt](https://hosts.gitcdn.top/hosts.txt) , 128 | 将其全部内容粘贴到你的hosts文件中,即可。 129 | 130 | - `Linux / MacOS` hosts路径:`/etc/hosts` 131 | - `Windows` hosts路径:`C:\Windows\System32\drivers\etc\hosts` 132 | 133 | #### 刷新生效 134 | 135 | - `Linux`: `/etc/init.d/network restart` 136 | - `Windows`: `ipconfig /flushdns` 137 | - `Macos`: `sudo killall -HUP mDNSResponder` 138 | 139 | #### Unix/Linux 一键使用 140 | 141 | ```shell 142 | sed -i "/# fetch-github-hosts begin/Q" /etc/hosts && curl https://hosts.gitcdn.top/hosts.txt >> /etc/hosts 143 | ``` 144 | 145 | > 提示:可以设置crontab定时任务定时获取更新即可,解放双手! 146 | 147 | ## 私有部署 148 | 149 | 下载最新的发行版(到 [Releases](https://github.com/Licoy/fetch-github-hosts/releases) 进行下载) 150 | ,并选择您的系统对应版本,直接以服务模式运行即可:`fetch-github-hosts -m=server -p=9898`,会自动监听`0.0.0.0:9898`,您可以直接浏览器访问 `http://127.0.0.1:9898` 151 | 以访问您自定义服务。 152 | (具体方法可参见【启动服务端】小节详细说明) 153 | 154 | > 注意:因网络影响,尽量部署到海外服务器节点! 155 | 156 | ## 趋势 157 | [![Stargazers over time](https://starchart.cc/Licoy/fetch-github-hosts.svg)](https://starchart.cc/Licoy/fetch-github-hosts) 158 | 159 | ## 开源协议 160 | 161 | [GPL 3.0](https://github.com/Licoy/fetch-github-hosts/blob/main/LICENSE) 162 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | [简体中文](./README.md) | English 2 | 3 |
4 |

Fetch GitHub Hosts

5 | 6 | ![LOGO](assets/public/logo.png) 7 | 8 | `fetch-github-hosts` is a `Github Hosts` synchronization tool mainly provided to solve the problem of slow access to `Github` or other problems for research and learning personnel. 9 | 10 | [![Release](https://img.shields.io/github/v/release/Licoy/fetch-github-hosts.svg?logo=git)](https://github.com/Licoy/fetch-github-hosts) 11 | [![Build Linux & Windows](https://github.com/Licoy/fetch-github-hosts/workflows/Build%20for%20Linux%20&%20Windows/badge.svg)](https://github.com/Licoy/fetch-github-hosts) 12 | [![Build MacOS](https://github.com/Licoy/fetch-github-hosts/workflows/Build%20for%20MacOS/badge.svg)](https://github.com/Licoy/fetch-github-hosts) 13 | 14 |
15 | 16 | ## no-gui build 17 | 18 | ```bash 19 | go build -tags="no_gui" 20 | ``` 21 | 22 | ## Principle 23 | 24 | This project obtains the `hosts` of `github.com` by deploying the server of the project itself, rather than through a third-party IP address interface, such as `ipaddress.com`, etc. 25 | 26 | ## Instructions 27 | ### Graphical interface 28 | Go to [Releases](https://github.com/Licoy/fetch-github-hosts/releases) to download your system version (currently supports `Windows`/`Linux`/`MacOS` 29 | ) 30 | 31 | After the download is completed, unzip the `tar.gz` compressed package and run the executable file of the corresponding platform to run (⚠️ Note: Linux needs to be started with `sudo`, Windows and MacOS will automatically perform privilege escalation operations.) 32 | 33 | #### Client mode 34 | ![client](assets/public/docs/client.png) 35 | 36 | #### Client startup 37 | ![client-start](assets/public/docs/client-start.png) 38 | 39 | #### Client hosts source selection 40 | ![client-select](assets/public/docs/client-select.png) 41 | 42 | #### Client hosts are derived from customization 43 | ![client-custom](assets/public/docs/client-custom.png) 44 | 45 | #### Server mode 46 | ![server](assets/public/docs/server.png) 47 | 48 | ### Command line terminal 49 | 50 | Go to [Releases](https://github.com/Licoy/fetch-github-hosts/releases) to download your system version (currently supports `Windows`/`Linux`/`MacOS` 51 | ) 52 | 53 | #### Parameters 54 | 55 | | Parameter name | Abbreviation | Default value | Required | Description | 56 | |----------------|--------------|--------------------------------------|----------|--------------------------------------------------------| 57 | | `mode` | `m` | None | Yes | Startup mode `server` / `client` | 58 | | `interval` | `i` | 60 | No | Get the record value interval (minutes) | 59 | | `port` | `p` | 9898 | No | Service mode listening port to access the HTTP service | 60 | | `url` | `u` | `https://hosts.gitcdn.top/hosts.txt` | No | Client mode remote hosts get link | 61 | | `lang` | `l` | `zh-CN` | No | Interface language | 62 | 63 | #### Start the client: 64 | 65 | > Note: 66 | > 67 | > You need to use `sudo` to run under Linux; 68 | > 69 | > Windows and MacOS will automatically perform privilege escalation operations. 70 | 71 | - run directly 72 | 73 | ```bash 74 | #Linux/Macos 75 | sudo fetch-github-hosts -m=client 76 | 77 | # Windows 78 | fetch-github-hosts.exe -m=client 79 | ``` 80 | 81 | - Customize the acquisition time interval 82 | 83 | ```bash 84 | # Linux/Macos (obtained once every 10 minutes) 85 | sudo fetch-github-hosts -i=10 86 | 87 | # Windows (obtained once every 10 minutes) 88 | fetch-github-hosts.exe -i=10 89 | ``` 90 | 91 | - Customized get link 92 | 93 | ```bash 94 | #Linux/Macos 95 | sudo fetch-github-hosts -u=http://127.0.0.1:9898/hosts.json 96 | 97 | # Windows 98 | fetch-github-hosts.exe -u=http://127.0.0.1:9898/hosts.json 99 | ``` 100 | 101 | #### Start the server: 102 | 103 | - run directly 104 | 105 | ```bash 106 | #Linux/Macos 107 | fetch-github-hosts -m=server 108 | 109 | # Windows 110 | fetch-github-hosts.exe -m=server 111 | ``` 112 | 113 | - Custom listening port 114 | 115 | ```bash 116 | #Linux/Macos 117 | fetch-github-hosts -m=server -p=6666 118 | 119 | # Windows 120 | fetch-github-hosts.exe -m=server -p=6666 121 | ``` 122 | 123 | ### Manual 124 | 125 | #### Add hosts 126 | 127 | Visit [https://hosts.gitcdn.top/hosts.txt](https://hosts.gitcdn.top/hosts.txt) , 128 | Paste its entire contents into your hosts file. 129 | 130 | - `Linux/MacOS` hosts path: `/etc/hosts` 131 | - `Windows` hosts path: `C:\Windows\System32\drivers\etc\hosts` 132 | 133 | #### Refresh takes effect 134 | 135 | - `Linux`: `/etc/init.d/network restart` 136 | - `Windows`: `ipconfig /flushdns` 137 | - `Macos`: `sudo killall -HUP mDNSResponder` 138 | 139 | #### Unix/Linux one-click use 140 | 141 | ```shell 142 | sed -i "/# fetch-github-hosts begin/Q" /etc/hosts && curl https://hosts.gitcdn.top/hosts.txt >> /etc/hosts 143 | ``` 144 | 145 | > Tip: You can set up a crontab scheduled task to get updates regularly, freeing your hands! 146 | 147 | ## Private deployment 148 | 149 | Download the latest release (go to [Releases](https://github.com/Licoy/fetch-github-hosts/releases) to download) 150 | , and select the corresponding version of your system, and run it directly in service mode: `fetch-github-hosts -m=server -p=9898`, which will automatically monitor `0.0.0.0:9898`, and you can access it directly with a browser `http://127.0.0.1:9898` 151 | to access your customized services. 152 | (For specific methods, please refer to the section [Start Server] for detailed instructions) 153 | 154 | > Note: Due to network influence, try to deploy to overseas server nodes! 155 | 156 | ## Trend 157 | [![Stargazers over time](https://starchart.cc/Licoy/fetch-github-hosts.svg)](https://starchart.cc/Licoy/fetch-github-hosts) 158 | 159 | ## Open Source Agreement 160 | 161 | [GPL 3.0](https://github.com/Licoy/fetch-github-hosts/blob/main/LICENSE) -------------------------------------------------------------------------------- /active.en-US.toml: -------------------------------------------------------------------------------- 1 | About = "About" 2 | AboutContent = "# Introduce \n\n Fetch Github Hosts is a Github Hosts synchronization tool primarily designed to address \n\n issues such as slow access to Github or other issues for researchers and learners\n---\n# License\nGNU General Public License v3.0\n\n# Version" 3 | CheckUpdate = "Update Check" 4 | CheckUpdateFail = "Check for updates failed" 5 | CleanGithubHostsFail = "Clearing github records in hosts failed" 6 | CleanGithubHostsSuccess = "The github record in the hosts file has been successfully cleared" 7 | ClearHosts = "Clear hosts" 8 | ClientFetchHostsGetErrorLog = "Failed to obtain the latest hosts" 9 | ClientFetchHostsReadErrorLog = "Failed to read the latest hosts" 10 | ClientMode = "Client Mode" 11 | CurrentIsNewest = "Currently, it is the latest version" 12 | DownloadNow = "Download Now" 13 | Feedback = "Feedback" 14 | FetchGithubHostsFail = "Failed to obtain the Host for Github" 15 | GetHostRecordErr = "Failed to obtain host records" 16 | GetIntervalMinutes = "Get interval (minutes)" 17 | GetIntervalNeedInt = "Get interval must be an integer" 18 | HostsOptCustom = "Custom hosts source" 19 | HostsOptOfficial = "Official designated hosts source" 20 | HostsOrigin = "Hosts origin" 21 | LangChangeTips = "The language has been switched to {{.Lang}} and will take effect the next time you start the program!" 22 | ListeningAddress = "Listening address {{.Addr}}" 23 | ListeningAddressWait = "Listening address: To be started" 24 | LogCreatedFail = "Log file creation failed" 25 | NetworkRequestFail = "Network request error" 26 | Ok = "OK" 27 | OpenHome = "Open home" 28 | ParseDomainsJsonErr = "domain.json parsing failed" 29 | ParseUpdateResponseFail = "Failed to parse and update response content" 30 | PermissionCheckFail = "Failed to check hosts read and write permissions, please run this program as sudo or administrator!" 31 | PortMustBeInt = "Port number must be an integer" 32 | ReadDomainsJsonErr = "Error reading file domains.json" 33 | ReadHostsErr = "Error reading file hosts" 34 | ReadIndexFileErr = "Failed to read homepage template file" 35 | ReadUpdateResponseFail = "Failed to read update response content" 36 | RemoteHostsFetchErrorLog = "Failed to update Github Hosts: {{.E}}" 37 | RemoteHostsFetchStopLog = "Stop obtaining hosts" 38 | RemoteHostsFetchSuccessLog = "Successfully updated Github Hosts!" 39 | RemoteHostsUrl = "Remote hosts url" 40 | RemoteHostsUrlLog = "Remote hosts obtaining links: {{.Url}}" 41 | RequestFail = "request failure" 42 | RunAsAdminUnix = "Please use the root account or sudo to execute this program!" 43 | RunAsAdminWin = "Please right-click and select [Run as administrator] to execute this program!" 44 | ServerFetchHostsErrorLog = "Failed to execute update Github Hosts: {{.E}}" 45 | ServerFetchHostsStopErrorLog = "Failed to close port listening" 46 | ServerFetchHostsStopLog = "Stopping updating hosts service" 47 | ServerFetchHostsStopSuccessLog = "Stopped updating hosts service" 48 | ServerFetchHostsSuccessLog = "Successfully executed update Github Hosts!" 49 | ServerFetchIndexFileErr = "Failed to retrieve homepage file: {{.E}}" 50 | ServerMode = "Server Mode" 51 | ServerStartErrorLog = "Service startup failed (possibly due to the target port being occupied): {{.E}}" 52 | ServerStartSuccessHostsJsonLinkLog = "JSON format link for hosts: http://127.0.0.1:{{.Port}}/hosts.json" 53 | ServerStartSuccessHostsLinkLog = "Hosts file link: http://127.0.0.1:{{.Port}}/hosts.txt" 54 | ServerStartSuccessLog = "Successfully listened to HTTP service:http://127.0.0.1:{{.Port}}" 55 | Start = "Start" 56 | StartupAutoGet = "Startup auto start" 57 | StartupAutoGetTips = "The automatic acquisition status of the startup software has changed and will take effect the next time the program is launched!" 58 | StartupPort = "Start port" 59 | Stop = "Stop" 60 | Tip = "Tip" 61 | UpdateLater = "Update later" 62 | UpdateTip = "Update Tip" 63 | UpdateTipContent = "Detected a new version, do you need to download the latest version immediately?" 64 | VersionParseFail = "Version number parsing failed" 65 | WriteHostsJsonFileErr = "Writing data to the hosts.json file failed" 66 | WriteHostsNoPermission = "Writing hosts file failed, please start this program as a super administrator!" 67 | WriteHostsTxtFileErr = "Writing data to the hosts.txt file failed" 68 | WriteIndexFileErr = "Failed to write update information to the homepage file" 69 | -------------------------------------------------------------------------------- /active.zh-CN.toml: -------------------------------------------------------------------------------- 1 | About = "关于" 2 | AboutContent = "# 介绍\nFetch Github Hosts是主要为解决研究及学习人员访问Github过慢或其他问题而提供的Github Hosts同步工具\n---\n# 开源协议\nGNU General Public License v3.0\n\n# 版本号" 3 | CheckUpdate = "检查更新" 4 | CheckUpdateFail = "检查更新失败" 5 | CleanGithubHostsFail = "清除hosts中的github记录失败" 6 | CleanGithubHostsSuccess = "hosts文件中的github记录已经清除成功" 7 | ClearHosts = "清除hosts" 8 | ClientFetchHostsGetErrorLog = "获取最新的hosts失败" 9 | ClientFetchHostsReadErrorLog = "读取最新的hosts失败" 10 | ClientMode = "客户端模式" 11 | CurrentIsNewest = "当前已是最新版本" 12 | DownloadNow = "立即下载" 13 | Feedback = "反馈建议" 14 | FetchGithubHostsFail = "获取Github的Host失败" 15 | GetHostRecordErr = "获取主机记录失败" 16 | GetIntervalMinutes = "获取间隔(分钟)" 17 | GetIntervalNeedInt = "获取间隔必须为整数" 18 | HostsOptCustom = "自定义hosts源" 19 | HostsOptOfficial = "官方指定hosts源" 20 | HostsOrigin = "Hosts源" 21 | LangChangeTips = "语言已经切换为 {{.Lang}},将会在下次启动程序时生效!" 22 | ListeningAddress = "监听地址 {{.Addr}}" 23 | ListeningAddressWait = "监听地址:待启动" 24 | LogCreatedFail = "日志文件创建失败" 25 | NetworkRequestFail = "网络请求错误" 26 | Ok = "确认" 27 | OpenHome = "打开主界面" 28 | ParseDomainsJsonErr = "domain.json解析失败" 29 | ParseUpdateResponseFail = "解析更新响应内容失败" 30 | PermissionCheckFail = "检查hosts读写权限失败,请以sudo或管理员身份来运行本程序!" 31 | PortMustBeInt = "端口号必须为整数" 32 | ReadDomainsJsonErr = "读取文件domains.json错误" 33 | ReadHostsErr = "读取文件hosts错误" 34 | ReadIndexFileErr = "读取首页模板文件失败" 35 | ReadUpdateResponseFail = "读取更新响应内容失败" 36 | RemoteHostsFetchErrorLog = "更新Github-Hosts失败: {{.E}}" 37 | RemoteHostsFetchStopLog = "停止获取hosts" 38 | RemoteHostsFetchSuccessLog = "更新Github-Hosts成功!" 39 | RemoteHostsUrl = "远程Hosts链接" 40 | RemoteHostsUrlLog = "远程hosts获取链接: {{.Url}}" 41 | RequestFail = "请求失败" 42 | RunAsAdminUnix = "请以root账户或sudo来执行本程序!" 43 | RunAsAdminWin = "请鼠标右键选择【以管理员的身份运行】来执行本程序!" 44 | ServerFetchHostsErrorLog = "执行更新Github-Hosts失败:{{.E}}" 45 | ServerFetchHostsStopErrorLog = "关闭端口监听失败" 46 | ServerFetchHostsStopLog = "正在停止更新hosts服务" 47 | ServerFetchHostsStopSuccessLog = "已停止更新hosts服务" 48 | ServerFetchHostsSuccessLog = "执行更新Github-Hosts成功!" 49 | ServerFetchIndexFileErr = "获取首页文件失败:{{.E}}" 50 | ServerMode = "服务端模式" 51 | ServerStartErrorLog = "服务启动失败(可能是目标端口已被占用):{{.E}}" 52 | ServerStartSuccessHostsJsonLinkLog = "hosts的JSON格式链接:http://127.0.0.1:{{.Port}}/hosts.json" 53 | ServerStartSuccessHostsLinkLog = "hosts文件链接:http://127.0.0.1:{{.Port}}/hosts.txt" 54 | ServerStartSuccessLog = "已监听HTTP服务成功:http://127.0.0.1:{{.Port}}" 55 | Start = "启动" 56 | StartupAutoGet = "启动软件自动获取" 57 | StartupAutoGetTips = "启动软件自动获取状态已改变,将会在下次启动程序时生效!" 58 | StartupPort = "启动端口号" 59 | Stop = "停止" 60 | Tip = "提示" 61 | UpdateLater = "稍后更新" 62 | UpdateTip = "更新提示" 63 | UpdateTipContent = "检测到有新的版本,是否立即需要去下载最新版本?" 64 | VersionParseFail = "版本号解析失败" 65 | WriteHostsJsonFileErr = "写入数据到hosts.json文件失败" 66 | WriteHostsNoPermission = "写入hosts文件失败,请用超级管理员身份启动本程序!" 67 | WriteHostsTxtFileErr = "写入数据到hosts.txt文件失败" 68 | WriteIndexFileErr = "写入更新信息到首页文件失败" 69 | -------------------------------------------------------------------------------- /args.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/jessevdk/go-flags" 7 | "os" 8 | ) 9 | 10 | type CmdArgs struct { 11 | Mode string `short:"m" long:"mode" description:"启动模式(client或server)"` 12 | FetchInterval int `default:"60" short:"i" long:"interval" description:"获取hosts的间隔时间,单位为分钟"` 13 | Port int `default:"9898" short:"p" long:"port" description:"服务模式监听端口"` 14 | Url string `default:"https://hosts.gitcdn.top/hosts.txt" short:"u" long:"url" description:"客户端模式远程hosts获取链接"` 15 | Escalate bool `long:"escalate" description:"提权执行"` 16 | DontEscalate bool `long:"de" description:"禁止提权执行"` 17 | Version bool `short:"v" long:"version" description:"查看当前版本"` 18 | Lang string `long:"lang" short:"l" description:"使用语言"` 19 | } 20 | 21 | func ParseBootArgs() *CmdArgs { 22 | args := &CmdArgs{} 23 | _, err := flags.ParseArgs(args, os.Args) 24 | if err != nil { 25 | et, y := err.(*flags.Error) 26 | if y { 27 | if errors.Is(flags.ErrHelp, et.Type) { 28 | os.Exit(0) 29 | } 30 | if !errors.Is(flags.ErrUnknownFlag, et.Type) { 31 | panic(fmt.Sprintf("解析参数错误: %v", err)) 32 | } 33 | } 34 | } 35 | if args.Version { 36 | fmt.Printf("版本号: V%.1f\n", VERSION) 37 | os.Exit(0) 38 | } 39 | if args.Mode != "" && (args.Mode != "client" && args.Mode != "server") { 40 | fmt.Printf("无效的启动模式: %s,已自动设置为client\n", args.Mode) 41 | args.Mode = "client" 42 | } 43 | if args.FetchInterval < 1 { 44 | fmt.Printf("获取hosts的间隔时间不可以小于1分钟,当前为%d分钟,已自动设置为60分钟\n", args.FetchInterval) 45 | args.FetchInterval = 60 46 | } 47 | return args 48 | } 49 | -------------------------------------------------------------------------------- /assets/domains.json: -------------------------------------------------------------------------------- 1 | [ 2 | "alive.github.com", 3 | "live.github.com", 4 | "github.githubassets.com", 5 | "central.github.com", 6 | "desktop.githubusercontent.com", 7 | "assets-cdn.github.com", 8 | "camo.githubusercontent.com", 9 | "github.map.fastly.net", 10 | "github.global.ssl.fastly.net", 11 | "gist.github.com", 12 | "github.io", 13 | "github.com", 14 | "github.blog", 15 | "api.github.com", 16 | "raw.githubusercontent.com", 17 | "user-images.githubusercontent.com", 18 | "favicons.githubusercontent.com", 19 | "avatars5.githubusercontent.com", 20 | "avatars4.githubusercontent.com", 21 | "avatars3.githubusercontent.com", 22 | "avatars2.githubusercontent.com", 23 | "avatars1.githubusercontent.com", 24 | "avatars0.githubusercontent.com", 25 | "avatars.githubusercontent.com", 26 | "codeload.github.com", 27 | "github-cloud.s3.amazonaws.com", 28 | "github-com.s3.amazonaws.com", 29 | "github-production-release-asset-2e65be.s3.amazonaws.com", 30 | "github-production-user-asset-6210df.s3.amazonaws.com", 31 | "github-production-repository-file-5c1aeb.s3.amazonaws.com", 32 | "githubstatus.com", 33 | "github.community", 34 | "github.dev", 35 | "collector.github.com", 36 | "pipelines.actions.githubusercontent.com", 37 | "media.githubusercontent.com", 38 | "cloud.githubusercontent.com", 39 | "objects.githubusercontent.com" 40 | ] -------------------------------------------------------------------------------- /assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fetch Github Hosts 8 | 9 | 10 | 11 |
12 |

Fetch GitHub Hosts

13 |

LOGO

14 |

fetch-github-hosts 是主要为解决研究及学习人员访问 Github 过慢或其他问题而提供的 Github Hosts 同步工具

15 |

Release 16 | Build Linux & Windows 17 | Build MacOS 18 |

19 |

原理

20 |

此项目是通过部署此项目本身的服务器来获取 github.comhosts,而不是通过第三方ip地址接口来进行获取,例如 ipaddress.com 等。

21 |

最近获取时间: 22 | 23 | 24 | 25 |   26 | [hosts.txt] 27 | [hosts.json] 28 |

29 |

使用方法

30 |

图形化界面

31 |

Releases 中下载您的系统版本(目前支持Windows/Linux/MacOS 32 | )

33 |

下载完成解压tar.gz压缩包,运行对应平台的执行文件即可运行( ⚠️ 注意:Linux下需要用sudo进行启动,Windows和MacOS会自动进行提权操作。)

34 |

客户端模式

35 |

client

36 |

客户端启动

37 |

client-start

38 |

客户端hosts源选择

39 |

client-select

40 |

客户端hosts源自定义

41 |

client-custom

42 |

服务端模式

43 |

server

44 |

命令行终端

45 |

Releases 中下载您的系统版本(目前支持Windows/Linux/MacOS 46 | )

47 |

参数

48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
参数名缩写默认值必填描述
modem启动模式 server(服务端) / client(客户端)
intervali60获取记录值间隔(分钟)
portp9898服务模式监听端口以访问HTTP服务
urluhttps://hosts.gitcdn.top/hosts.txt客户端模式远程hosts获取链接
89 |

启动客户端:

90 |
91 |

注意:

92 |

Linux下需要使用sudo运行;

93 |

Windows和MacOS会自动进行提权操作。

94 |
95 |
    96 |
  • 直接运行
  • 97 |
98 |
# Linux/Macos
 99 | sudo fetch-github-hosts -m=client
100 | 
101 | # Windows
102 | fetch-github-hosts.exe -m=client
103 |
    104 |
  • 自定义获取时间间隔
  • 105 |
106 |
# Linux/Macos(10分钟获取一次)
107 | sudo fetch-github-hosts -i=10
108 | 
109 | # Windows(10分钟获取一次)
110 | fetch-github-hosts.exe -i=10
111 |
    112 |
  • 自定义获取链接
  • 113 |
114 |
# Linux/Macos
115 | sudo fetch-github-hosts -u=http://127.0.0.1:9898/hosts.json
116 | 
117 | # Windows
118 | fetch-github-hosts.exe -u=http://127.0.0.1:9898/hosts.json
119 |

启动服务端:

120 |
    121 |
  • 直接运行
  • 122 |
123 |
# Linux/Macos
124 | fetch-github-hosts -m=server
125 | 
126 | # Windows
127 | fetch-github-hosts.exe -m=server
128 |
    129 |
  • 自定义监听端口
  • 130 |
131 |
# Linux/Macos
132 | fetch-github-hosts -m=server -p=6666
133 | 
134 | # Windows
135 | fetch-github-hosts.exe -m=server -p=6666
136 |

手动

137 |

添加hosts

138 |

访问 https://hosts.gitcdn.top/hosts.txt , 139 | 将其全部内容粘贴到你的hosts文件中,即可。

140 |
    141 |
  • Linux / MacOS hosts路径:/etc/hosts
  • 142 |
  • Windows hosts路径:C:\Windows\System32\drivers\etc\hosts
  • 143 |
144 |

刷新生效

145 |
    146 |
  • Linux: /etc/init.d/network restart
  • 147 |
  • Windows: ipconfig /flushdns
  • 148 |
  • Macos: sudo killall -HUP mDNSResponder
  • 149 |
150 |

Unix/Linux 一键使用

151 |
sed -i "/# fetch-github-hosts begin/Q" /etc/hosts && curl https://hosts.gitcdn.top/hosts.txt >> /etc/hosts
152 |
153 |

提示:可以设置crontab定时任务定时获取更新即可,解放双手!

154 |
155 |

私有部署

156 |

下载最新的发行版(到 Releases 进行下载) 157 | ,并选择您的系统对应版本,直接以服务模式运行即可:fetch-github-hosts -m=server -p=9898,会自动监听0.0.0.0:9898,您可以直接浏览器访问 http://127.0.0.1:9898 158 | 以访问您自定义服务。 159 | (具体方法可参见【启动服务端】小节详细说明)

160 |
161 |

注意:因网络影响,尽量部署到海外服务器节点!

162 |
163 |

开源协议

164 |

GPL 3.0

165 | 166 | -------------------------------------------------------------------------------- /assets/public/docs/client-custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/docs/client-custom.png -------------------------------------------------------------------------------- /assets/public/docs/client-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/docs/client-select.png -------------------------------------------------------------------------------- /assets/public/docs/client-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/docs/client-start.png -------------------------------------------------------------------------------- /assets/public/docs/client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/docs/client.png -------------------------------------------------------------------------------- /assets/public/docs/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/docs/server.png -------------------------------------------------------------------------------- /assets/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/public/logo.png -------------------------------------------------------------------------------- /assets/public/style.min.css: -------------------------------------------------------------------------------- 1 | :root{--sans-font:-apple-system,BlinkMacSystemFont,"Avenir Next",Avenir,"Nimbus Sans L",Roboto,Noto,"Segoe UI",Arial,Helvetica,"Helvetica Neue",sans-serif;--mono-font:Consolas,Menlo,Monaco,"Andale Mono","Ubuntu Mono",monospace;--bg:#fff;--accent-bg:#f5f7ff;--text:#212121;--text-light:#585858;--border:#d8dae1;--accent:#0d47a1;--code:#d81b60;--preformatted:#444;--marked:#ffdd33;--disabled:#efefef}@media (prefers-color-scheme:dark){:root{--bg:#212121;--accent-bg:#2b2b2b;--text:#dcdcdc;--text-light:#ababab;--border:#666;--accent:#ffb300;--code:#f06292;--preformatted:#ccc;--disabled:#111}img,video{opacity:.8}}html{font-family:var(--sans-font);scroll-behavior:smooth}body{color:var(--text);background:var(--bg);font-size:1.15rem;line-height:1.5;display:grid;grid-template-columns:1fr min(45rem,90%) 1fr;margin:0}body>*{grid-column:2}body>header{background:var(--accent-bg);border-bottom:1px solid var(--border);text-align:center;padding:0 .5rem 2rem .5rem;grid-column:1/-1;box-sizing:border-box}body>header h1{max-width:1200px;margin:1rem auto}body>header p{max-width:40rem;margin:1rem auto}main{padding-top:1.5rem}body>footer{margin-top:4rem;padding:2rem 1rem 1.5rem 1rem;color:var(--text-light);font-size:.9rem;text-align:center;border-top:1px solid var(--border)}h1{font-size:3rem}h2{font-size:2.6rem;margin-top:3rem}h3{font-size:2rem;margin-top:3rem}h4{font-size:1.44rem}h5{font-size:1.15rem}h6{font-size:.96rem}h1,h2,h3{line-height:1.1}@media only screen and (max-width:720px){h1{font-size:2.5rem}h2{font-size:2.1rem}h3{font-size:1.75rem}h4{font-size:1.25rem}}a,a:visited{color:var(--accent)}a:hover{text-decoration:none}[role=button],button,input[type=button],input[type=reset],input[type=submit]{border:none;border-radius:5px;background:var(--accent);font-size:1rem;color:var(--bg);padding:.7rem .9rem;margin:.5rem 0}[role=button][aria-disabled=true],button[disabled],input[type=button][disabled],input[type=checkbox][disabled],input[type=radio][disabled],input[type=reset][disabled],input[type=submit][disabled],select[disabled]{opacity:.5;cursor:not-allowed}input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;background-color:var(--disabled)}input[type=range]{padding:0}abbr{cursor:help}[role=button]:focus,[role=button]:not([aria-disabled=true]):hover,button:enabled:hover,button:focus,input[type=button]:enabled:hover,input[type=button]:focus,input[type=reset]:enabled:hover,input[type=reset]:focus,input[type=submit]:enabled:hover,input[type=submit]:focus{filter:brightness(1.4);cursor:pointer}header>nav{font-size:1rem;line-height:2;padding:1rem 0 0 0}header>nav ol,header>nav ul{align-content:space-around;align-items:center;display:flex;flex-direction:row;justify-content:center;list-style-type:none;margin:0;padding:0}header>nav ol li,header>nav ul li{display:inline-block}header>nav a,header>nav a:visited{margin:0 1rem 1rem 0;border:1px solid var(--border);border-radius:5px;color:var(--text);display:inline-block;padding:.1rem 1rem;text-decoration:none}header>nav a:hover{color:var(--accent);border-color:var(--accent)}header>nav a:last-child{margin-right:0}@media only screen and (max-width:720px){header>nav a{border:none;padding:0;color:var(--accent);text-decoration:underline;line-height:1}}aside{width:30%;padding:0 15px;margin-left:15px;float:right;background:var(--accent-bg);border:1px solid var(--border);border-radius:5px}article{border:1px solid var(--border);padding:1rem;border-radius:5px}article h2:first-child,section h2:first-child{margin-top:1rem}section{border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:2rem 1rem;margin:3rem 0}details{background:var(--accent-bg);border:1px solid var(--border);border-radius:5px;margin-bottom:1rem}summary{cursor:pointer;font-weight:700;padding:.6rem 1rem}details[open]{padding:.6rem 1rem .75rem 1rem}details[open] summary+*{margin-top:0}details[open] summary{margin-bottom:.5rem;padding:0}details[open]>:last-child{margin-bottom:0}table{border-collapse:collapse;display:block;margin:1.5rem 0;overflow:auto;width:100%}td,th{border:1px solid var(--border);text-align:left;padding:.5rem}th{background:var(--accent-bg);font-weight:700}tr:nth-child(even){background:var(--accent-bg)}table caption{font-weight:700;margin-bottom:.5rem}input,select,textarea{font-size:inherit;font-family:inherit;padding:.5rem;margin-bottom:.5rem;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:5px;box-shadow:none;box-sizing:border-box;width:60%;-moz-appearance:none;-webkit-appearance:none;appearance:none}select{background-image:linear-gradient(45deg,transparent 49%,var(--text) 51%),linear-gradient(135deg,var(--text) 51%,transparent 49%);background-position:calc(100% - 20px),calc(100% - 15px);background-size:5px 5px,5px 5px;background-repeat:no-repeat}select[multiple]{background-image:none!important}input[type=checkbox],input[type=radio]{vertical-align:bottom;position:relative}input[type=radio]{border-radius:100%}input[type=checkbox]:checked,input[type=radio]:checked{background:var(--accent)}input[type=checkbox]:checked::after{content:" ";width:.1em;height:.25em;border-radius:0;position:absolute;top:.05em;left:.18em;background:0 0;border-right:solid var(--bg) .08em;border-bottom:solid var(--bg) .08em;font-size:1.8em;transform:rotate(45deg)}input[type=radio]:checked::after{content:" ";width:.25em;height:.25em;border-radius:100%;position:absolute;top:.125em;background:var(--bg);left:.125em;font-size:32px}textarea{width:80%}@media only screen and (max-width:720px){input,select,textarea{width:100%}}input[type=checkbox],input[type=radio]{width:auto}input[type=file]{border:0}hr{color:var(--border);border-top:1px;margin:1rem auto}mark{padding:2px 5px;border-radius:4px;background:var(--marked)}main img,main video{max-width:100%;height:auto;border-radius:5px}figure{margin:0;text-align:center}figcaption{font-size:.9rem;color:var(--text-light);margin-bottom:1rem}blockquote{margin:2rem 0 2rem 2rem;padding:.4rem .8rem;border-left:.35rem solid var(--accent);color:var(--text-light);font-style:italic}cite{font-size:.9rem;color:var(--text-light);font-style:normal}code,kbd,pre,pre span,samp{font-family:var(--mono-font);color:var(--code)}kbd{color:var(--preformatted);border:1px solid var(--preformatted);border-bottom:3px solid var(--preformatted);border-radius:5px;padding:.1rem .4rem}pre{padding:1rem 1.4rem;max-width:100%;overflow:auto;color:var(--preformatted);background:var(--accent-bg);border:1px solid var(--border);border-radius:5px}pre code{color:var(--preformatted);background:0 0;margin:0;padding:0}body p img{max-width: 100%} 2 | 3 | -------------------------------------------------------------------------------- /assets/zcool-cryyt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Licoy/fetch-github-hosts/dbafac25cb2187e4068b749721fd0a5222d06654/assets/zcool-cryyt.ttf -------------------------------------------------------------------------------- /conf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | ) 6 | 7 | type FetchConf struct { 8 | Lang string 9 | Client struct { 10 | Interval int 11 | Method string 12 | SelectOrigin string 13 | CustomUrl string 14 | AutoFetch bool 15 | } 16 | Server struct { 17 | Interval int 18 | Port int 19 | } 20 | } 21 | 22 | func (f *FetchConf) Storage() { 23 | viper.Set("lang", f.Lang) 24 | viper.Set("client.interval", f.Client.Interval) 25 | viper.Set("client.method", f.Client.Method) 26 | viper.Set("client.selectorigin", f.Client.SelectOrigin) 27 | viper.Set("client.customurl", f.Client.CustomUrl) 28 | viper.Set("client.autofetch", f.Client.AutoFetch) 29 | viper.Set("server.interval", f.Server.Interval) 30 | viper.Set("server.port", f.Server.Port) 31 | if err := viper.WriteConfigAs("conf.yaml"); err != nil { 32 | _fileLog.Print("持久化配置信息失败:" + err.Error()) 33 | } 34 | } 35 | 36 | func LoadFetchConf() *FetchConf { 37 | viper.AddConfigPath(AppExecDir()) 38 | viper.SetConfigName("conf") 39 | viper.SetConfigType("yaml") 40 | viper.SetDefault("lang", "zh-CN") 41 | viper.SetDefault("client.interval", 60) 42 | viper.SetDefault("client.method", "官方指定hosts源") 43 | viper.SetDefault("client.selectorigin", "FetchGithubHosts") 44 | viper.SetDefault("client.autofetch", false) 45 | viper.SetDefault("server.interval", 60) 46 | viper.SetDefault("server.port", 9898) 47 | var fileNotExits bool 48 | if err := viper.ReadInConfig(); err != nil { 49 | if _, ok := err.(viper.ConfigFileNotFoundError); ok { 50 | fileNotExits = true 51 | } else { 52 | _fileLog.Print("加载配置文件错误: " + err.Error()) 53 | } 54 | } 55 | res := FetchConf{} 56 | if err := viper.Unmarshal(&res); err != nil { 57 | _fileLog.Print("配置文件解析失败") 58 | } 59 | if fileNotExits { 60 | res.Storage() 61 | } 62 | return &res 63 | } 64 | -------------------------------------------------------------------------------- /consts.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | GuiFontName = "zcool-cryyt.ttf" 5 | ) 6 | 7 | var HostsOrigins = map[string]string{ 8 | "FetchGithubHosts": "https://hosts.gitcdn.top/hosts.txt", 9 | "Github520": "https://raw.hellogithub.com/hosts", 10 | } 11 | -------------------------------------------------------------------------------- /fetch_hosts.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/h2non/filetype" 8 | "github.com/nicksnyder/go-i18n/v2/i18n" 9 | "io" 10 | "net" 11 | "net/http" 12 | "os" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | const ( 18 | Windows = "windows" 19 | Linux = "linux" 20 | Darwin = "darwin" 21 | ) 22 | 23 | func startClient(ticker *FetchTicker, url string, flog *FetchLog) { 24 | flog.Print(tfs(&i18n.Message{ 25 | ID: "RemoteHostsUrlLog", 26 | Other: "远程hosts获取链接: {{.Url}}", 27 | }, map[string]interface{}{ 28 | "Url": url, 29 | })) 30 | fn := func() { 31 | if err := ClientFetchHosts(url); err != nil { 32 | flog.Print(tfs(&i18n.Message{ 33 | ID: "RemoteHostsFetchErrorLog", 34 | Other: "更新Github-Hosts失败: {{.E}}", 35 | }, map[string]interface{}{ 36 | "E": err.Error(), 37 | })) 38 | } else { 39 | flog.Print(t(&i18n.Message{ 40 | ID: "RemoteHostsFetchSuccessLog", 41 | Other: "更新Github-Hosts成功!", 42 | })) 43 | } 44 | } 45 | fn() 46 | for { 47 | select { 48 | case <-ticker.Ticker.C: 49 | fn() 50 | case <-ticker.CloseChan: 51 | flog.Print(t(&i18n.Message{ 52 | ID: "RemoteHostsFetchStopLog", 53 | Other: "停止获取hosts", 54 | })) 55 | return 56 | } 57 | } 58 | } 59 | 60 | func startServer(ticker *FetchTicker, port int, flog *FetchLog) { 61 | listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) 62 | if err != nil { 63 | flog.Print(tfs(&i18n.Message{ 64 | ID: "ServerStartErrorLog", 65 | Other: "服务启动失败(可能是目标端口已被占用):{{.E}}", 66 | }, map[string]interface{}{ 67 | "E": err.Error(), 68 | })) 69 | return 70 | } 71 | flog.Print(tfs(&i18n.Message{ 72 | ID: "ServerStartSuccessLog", 73 | Other: "已监听HTTP服务成功:http://127.0.0.1:{{.Port}}", 74 | }, map[string]interface{}{ 75 | "Port": port, 76 | })) 77 | flog.Print(tfs(&i18n.Message{ 78 | ID: "ServerStartSuccessHostsLinkLog", 79 | Other: "hosts文件链接:http://127.0.0.1:{{.Port}}/hosts.txt", 80 | }, map[string]interface{}{ 81 | "Port": port, 82 | })) 83 | flog.Print(tfs(&i18n.Message{ 84 | ID: "ServerStartSuccessHostsJsonLinkLog", 85 | Other: "hosts的JSON格式链接:http://127.0.0.1:{{.Port}}/hosts.json", 86 | }, map[string]interface{}{ 87 | "Port": port, 88 | })) 89 | go http.Serve(listen, &serverHandle{flog}) 90 | fn := func() { 91 | if err := ServerFetchHosts(); err != nil { 92 | flog.Print(tfs(&i18n.Message{ 93 | ID: "ServerFetchHostsErrorLog", 94 | Other: "执行更新Github-Hosts失败:{{.E}}", 95 | }, map[string]interface{}{ 96 | "E": err.Error(), 97 | })) 98 | } else { 99 | flog.Print(t(&i18n.Message{ 100 | ID: "ServerFetchHostsSuccessLog", 101 | Other: "执行更新Github-Hosts成功!", 102 | })) 103 | } 104 | } 105 | fn() 106 | for { 107 | select { 108 | case <-ticker.Ticker.C: 109 | fn() 110 | case <-ticker.CloseChan: 111 | flog.Print(t(&i18n.Message{ 112 | ID: "ServerFetchHostsStopLog", 113 | Other: "正在停止更新hosts服务", 114 | })) 115 | if err := listen.Close(); err != nil { 116 | flog.Print(t(&i18n.Message{ 117 | ID: "ServerFetchHostsStopErrorLog", 118 | Other: "关闭端口监听失败", 119 | })) 120 | } 121 | flog.Print(t(&i18n.Message{ 122 | ID: "ServerFetchHostsStopSuccessLog", 123 | Other: "已停止更新hosts服务", 124 | })) 125 | return 126 | } 127 | } 128 | } 129 | 130 | type serverHandle struct { 131 | flog *FetchLog 132 | } 133 | 134 | func (s *serverHandle) ServeHTTP(resp http.ResponseWriter, request *http.Request) { 135 | p := request.URL.Path 136 | if p == "/" || p == "/hosts.txt" || p == "/hosts.json" { 137 | if p == "/" { 138 | p = "/index.html" 139 | } 140 | file, err := os.ReadFile(AppExecDir() + p) 141 | if err != nil { 142 | resp.WriteHeader(http.StatusInternalServerError) 143 | resp.Write([]byte("server error")) 144 | s.flog.Print(tfs(&i18n.Message{ 145 | ID: "ServerFetchIndexFileErr", 146 | Other: "获取首页文件失败:{{.E}}", 147 | }, map[string]interface{}{ 148 | "E": err.Error(), 149 | })) 150 | return 151 | } 152 | resp.Write(file) 153 | return 154 | } 155 | if strings.HasPrefix(p, "/public/") { 156 | file, _ := assetsFs.ReadFile("assets" + p) 157 | kind, _ := filetype.Match(file) 158 | resp.Header().Set("Content-Type", kind.MIME.Value) 159 | resp.Write(file) 160 | return 161 | } 162 | http.Redirect(resp, request, "/", http.StatusMovedPermanently) 163 | } 164 | 165 | // ClientFetchHosts 获取最新的host并写入hosts文件 166 | func ClientFetchHosts(url string) (err error) { 167 | hosts, err := getCleanGithubHosts() 168 | if err != nil { 169 | return 170 | } 171 | 172 | resp, err := http.Get(url) 173 | if err != nil || resp.StatusCode != http.StatusOK { 174 | err = ComposeError(t(&i18n.Message{ 175 | ID: "ClientFetchHostsGetErrorLog", 176 | Other: "获取最新的hosts失败", 177 | }), err) 178 | return 179 | } 180 | 181 | fetchHosts, err := io.ReadAll(resp.Body) 182 | if err != nil { 183 | err = ComposeError(t(&i18n.Message{ 184 | ID: "ClientFetchHostsReadErrorLog", 185 | Other: "读取最新的hosts失败", 186 | }), err) 187 | return 188 | } 189 | 190 | newlineChar := GetNewlineChar() 191 | 192 | fetchHostsLines := strings.Split(string(fetchHosts), "\n") 193 | 194 | for i, fetchLine := range fetchHostsLines { 195 | line := strings.TrimSpace(fetchLine) 196 | if line == "" || strings.HasPrefix(line, "#") { 197 | continue 198 | } 199 | hosts.WriteString(fetchLine) 200 | if i != len(fetchHostsLines)-1 { 201 | hosts.WriteString(newlineChar) 202 | } 203 | } 204 | if err = os.WriteFile(GetSystemHostsPath(), hosts.Bytes(), os.ModeType); err != nil { 205 | err = ComposeError(t(&i18n.Message{ 206 | ID: "WriteHostsNoPermission", 207 | Other: "写入hosts文件失败,请用超级管理员身份启动本程序!", 208 | }), err) 209 | return 210 | } 211 | 212 | return 213 | } 214 | 215 | // ServerFetchHosts 服务端获取github最新的hosts并写入到对应文件及更新首页 216 | func ServerFetchHosts() (err error) { 217 | execDir := AppExecDir() 218 | domains, err := getGithubDomains() 219 | if err != nil { 220 | return 221 | } 222 | 223 | hostJson, hostFile, now, err := FetchHosts(domains) 224 | if err != nil { 225 | err = ComposeError(t(&i18n.Message{ 226 | ID: "FetchGithubHostsFail", 227 | Other: "获取Github的Host失败", 228 | }), err) 229 | return 230 | } 231 | 232 | if err = os.WriteFile(execDir+"/hosts.json", hostJson, 0775); err != nil { 233 | err = ComposeError(t(&i18n.Message{ 234 | ID: "WriteHostsJsonFileErr", 235 | Other: "写入数据到hosts.json文件失败", 236 | }), err) 237 | return 238 | } 239 | 240 | if err = os.WriteFile(execDir+"/hosts.txt", hostFile, 0775); err != nil { 241 | err = ComposeError(t(&i18n.Message{ 242 | ID: "WriteHostsTxtFileErr", 243 | Other: "写入数据到hosts.txt文件失败", 244 | }), err) 245 | return 246 | } 247 | 248 | var templateFile []byte 249 | templateFile, err = GetExecOrEmbedFile(&assetsFs, "assets/index.html") 250 | if err != nil { 251 | err = ComposeError(t(&i18n.Message{ 252 | ID: "ReadIndexFileErr", 253 | Other: "读取首页模板文件失败", 254 | }), err) 255 | return 256 | } 257 | 258 | templateData := strings.Replace(string(templateFile), "", now, 1) 259 | if err = os.WriteFile(execDir+"/index.html", []byte(templateData), 0775); err != nil { 260 | err = ComposeError(t(&i18n.Message{ 261 | ID: "WriteIndexFileErr", 262 | Other: "写入更新信息到首页文件失败", 263 | }), err) 264 | return 265 | } 266 | 267 | return 268 | } 269 | 270 | func FetchHosts(domains []string) (hostsJson, hostsFile []byte, now string, err error) { 271 | now = time.Now().Format("2006-01-02 15:04:05") 272 | hosts := make([][]string, 0, len(domains)) 273 | hostsFileData := bytes.NewBufferString("# fetch-github-hosts begin\n") 274 | for _, domain := range domains { 275 | ipList, sErr := net.LookupIP(domain) 276 | if sErr != nil { 277 | fmt.Printf("%s: %s\b", t(&i18n.Message{ 278 | ID: "GetHostRecordErr", 279 | Other: "获取主机记录失败", 280 | }), sErr.Error()) 281 | continue 282 | } 283 | for _, ip := range ipList { 284 | ipv4 := ip.To4() 285 | if ipv4 != nil { 286 | item := []string{ipv4.String(), domain} 287 | hosts = append(hosts, item) 288 | hostsFileData.WriteString(fmt.Sprintf("%-28s%s\n", item[0], item[1])) 289 | break 290 | } 291 | } 292 | } 293 | hostsFileData.WriteString("# last fetch time: ") 294 | hostsFileData.WriteString(now) 295 | hostsFileData.WriteString("\n# update url: https://hosts.gitcdn.top/hosts.txt\n# fetch-github-hosts end\n\n") 296 | hostsFile = hostsFileData.Bytes() 297 | hostsJson, err = json.Marshal(hosts) 298 | return 299 | } 300 | 301 | func getCleanGithubHosts() (hosts *bytes.Buffer, err error) { 302 | hostsPath := GetSystemHostsPath() 303 | hostsBytes, err := os.ReadFile(hostsPath) 304 | if err != nil { 305 | err = ComposeError(t(&i18n.Message{ 306 | ID: "ReadHostsErr", 307 | Other: "读取文件hosts错误", 308 | }), err) 309 | return 310 | } 311 | 312 | domains, err := getGithubDomains() 313 | if err != nil { 314 | return 315 | } 316 | 317 | newlineChar := GetNewlineChar() 318 | 319 | // clear local hosts github domain 320 | localHostsLines := strings.Split(string(hostsBytes), newlineChar) 321 | hosts = &bytes.Buffer{} 322 | 323 | for i, localLine := range localHostsLines { 324 | line := strings.TrimSpace(localLine) 325 | if line == "" || strings.HasPrefix(line, "#") { 326 | hosts.WriteString(localLine) 327 | if i != len(localHostsLines)-1 || !strings.HasSuffix(hosts.String(), newlineChar) { 328 | hosts.WriteString(newlineChar) 329 | } 330 | continue 331 | } 332 | var clearLine bool 333 | for _, domain := range domains { 334 | if strings.Contains(line, domain) { 335 | clearLine = true 336 | break 337 | } 338 | } 339 | if !clearLine { 340 | hosts.WriteString(localLine) 341 | hosts.WriteString(newlineChar) 342 | } 343 | } 344 | 345 | return 346 | } 347 | 348 | func getGithubDomains() (domains []string, err error) { 349 | fileData, err := GetExecOrEmbedFile(&assetsFs, "assets/domains.json") 350 | if err != nil { 351 | err = ComposeError(t(&i18n.Message{ 352 | ID: "ReadDomainsJsonErr", 353 | Other: "读取文件domains.json错误", 354 | }), err) 355 | return 356 | } 357 | 358 | if err = json.Unmarshal(fileData, &domains); err != nil { 359 | err = ComposeError(t(&i18n.Message{ 360 | ID: "ParseDomainsJsonErr", 361 | Other: "domain.json解析失败", 362 | }), err) 363 | return 364 | } 365 | return 366 | } 367 | 368 | func flushCleanGithubHosts() (err error) { 369 | hosts, err := getCleanGithubHosts() 370 | if err != nil { 371 | return 372 | } 373 | if err = os.WriteFile(GetSystemHostsPath(), hosts.Bytes(), os.ModeType); err != nil { 374 | err = ComposeError(t(&i18n.Message{ 375 | ID: "WriteHostsNoPermission", 376 | Other: "写入hosts文件失败,请用超级管理员身份启动本程序!", 377 | }), err) 378 | } 379 | return 380 | } 381 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Licoy/fetch-github-hosts 2 | 3 | go 1.18 4 | 5 | require ( 6 | fyne.io/fyne/v2 v2.4.3 7 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 8 | github.com/h2non/filetype v1.1.3 9 | github.com/jessevdk/go-flags v1.5.0 10 | github.com/nicksnyder/go-i18n/v2 v2.3.0 11 | github.com/pelletier/go-toml/v2 v2.1.0 12 | github.com/spf13/viper v1.18.2 13 | golang.org/x/text v0.14.0 14 | ) 15 | 16 | require ( 17 | fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // indirect 18 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 19 | github.com/fredbi/uri v1.0.0 // indirect 20 | github.com/fsnotify/fsnotify v1.7.0 // indirect 21 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect 22 | github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect 23 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect 24 | github.com/getlantern/byteexec v0.0.0-20220903142956-e6ed20032cfd // indirect 25 | github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 // indirect 26 | github.com/getlantern/errors v1.0.3 // indirect 27 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c // indirect 28 | github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 // indirect 29 | github.com/getlantern/hex v0.0.0-20220104173244-ad7e4b9194dc // indirect 30 | github.com/getlantern/hidden v0.0.0-20220104173330-f221c5a24770 // indirect 31 | github.com/getlantern/ops v0.0.0-20230519221840-1283e026181c // indirect 32 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect 33 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect 34 | github.com/go-logr/logr v1.2.4 // indirect 35 | github.com/go-logr/stdr v1.2.2 // indirect 36 | github.com/go-stack/stack v1.8.1 // indirect 37 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 // indirect 38 | github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a // indirect 39 | github.com/godbus/dbus/v5 v5.1.0 // indirect 40 | github.com/gopherjs/gopherjs v1.17.2 // indirect 41 | github.com/hashicorp/hcl v1.0.0 // indirect 42 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect 43 | github.com/magiconair/properties v1.8.7 // indirect 44 | github.com/mitchellh/mapstructure v1.5.0 // indirect 45 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect 46 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 47 | github.com/sagikazarmark/locafero v0.4.0 // indirect 48 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 49 | github.com/sourcegraph/conc v0.3.0 // indirect 50 | github.com/spf13/afero v1.11.0 // indirect 51 | github.com/spf13/cast v1.6.0 // indirect 52 | github.com/spf13/pflag v1.0.5 // indirect 53 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect 54 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect 55 | github.com/stretchr/testify v1.8.4 // indirect 56 | github.com/subosito/gotenv v1.6.0 // indirect 57 | github.com/tevino/abool v1.2.0 // indirect 58 | github.com/yuin/goldmark v1.5.5 // indirect 59 | go.opentelemetry.io/otel v1.16.0 // indirect 60 | go.opentelemetry.io/otel/metric v1.16.0 // indirect 61 | go.opentelemetry.io/otel/trace v1.16.0 // indirect 62 | go.uber.org/atomic v1.11.0 // indirect 63 | go.uber.org/multierr v1.11.0 // indirect 64 | go.uber.org/zap v1.24.0 // indirect 65 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 66 | golang.org/x/image v0.14.0 // indirect 67 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect 68 | golang.org/x/net v0.19.0 // indirect 69 | golang.org/x/sys v0.15.0 // indirect 70 | gopkg.in/ini.v1 v1.67.0 // indirect 71 | gopkg.in/yaml.v3 v3.0.1 // indirect 72 | honnef.co/go/js/dom v0.0.0-20231112215516-51f43a291193 // indirect 73 | ) 74 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 22 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 23 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 24 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 25 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 26 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 27 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 28 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 29 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 30 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 31 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 32 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 33 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 34 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 35 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 36 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 37 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 38 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 39 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 40 | fyne.io/fyne/v2 v2.4.3 h1:v2wncjEAcwXZ8UNmTCWTGL9+sGyPc5RuzBvM96GcC78= 41 | fyne.io/fyne/v2 v2.4.3/go.mod h1:1h3BKxmQYRJlr2g+RGVxedzr6vLVQ/AJmFWcF9CJnoQ= 42 | fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e h1:Hvs+kW2VwCzNToF3FmnIAzmivNgrclwPgoUdVSrjkP8= 43 | fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= 44 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 45 | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= 46 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 47 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 48 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 49 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 50 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 51 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 52 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 53 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 54 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 55 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 56 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 57 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 58 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 59 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 60 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 61 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 62 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 63 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 64 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 65 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 66 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 67 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 68 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 69 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 70 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 71 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 72 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 73 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 74 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 75 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 76 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 77 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 78 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 79 | github.com/fredbi/uri v1.0.0 h1:s4QwUAZ8fz+mbTsukND+4V5f+mJ/wjaTokwstGUAemg= 80 | github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0= 81 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 82 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 83 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 84 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4= 85 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= 86 | github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU= 87 | github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E= 88 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= 89 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= 90 | github.com/getlantern/byteexec v0.0.0-20220903142956-e6ed20032cfd h1:0xt9OTbV50a/+ZarMcr86ybWiN1v+bbwzdnVuXHzR/o= 91 | github.com/getlantern/byteexec v0.0.0-20220903142956-e6ed20032cfd/go.mod h1:oD9q9NB1LNBLHk3WAwza4tivxV7tm7jKFlCNCAv3+M8= 92 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= 93 | github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA= 94 | github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= 95 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 h1:q50MSzoIIKotG7apUYaDME/bNGhOJMjG33Fpfc7KPWM= 96 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264/go.mod h1:2VB8zy/kMNX347i5fdusJbPNAZE26u8qoHJDy7CWP9A= 97 | github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= 98 | github.com/getlantern/errors v1.0.3 h1:Ne4Ycj7NI1BtSyAfVeAT/DNoxz7/S2BUc3L2Ht1YSHE= 99 | github.com/getlantern/errors v1.0.3/go.mod h1:m8C7H1qmouvsGpwQqk/6NUpIVMpfzUPn608aBZDYV04= 100 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= 101 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= 102 | github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 h1:NlQedYmPI3pRAXJb+hLVVDGqfvvXGRPV8vp7XOjKAZ0= 103 | github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65/go.mod h1:+ZU1h+iOVqWReBpky6d5Y2WL0sF2Llxu+QcxJFs2+OU= 104 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= 105 | github.com/getlantern/hex v0.0.0-20220104173244-ad7e4b9194dc h1:sue+aeVx7JF5v36H1HfvcGFImLpSD5goj8d+MitovDU= 106 | github.com/getlantern/hex v0.0.0-20220104173244-ad7e4b9194dc/go.mod h1:D9RWpXy/EFPYxiKUURo2TB8UBosbqkiLhttRrZYtvqM= 107 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= 108 | github.com/getlantern/hidden v0.0.0-20220104173330-f221c5a24770 h1:cSrD9ryDfTV2yaur9Qk3rHYD414j3Q1rl7+L0AylxrE= 109 | github.com/getlantern/hidden v0.0.0-20220104173330-f221c5a24770/go.mod h1:GOQsoDnEHl6ZmNIL+5uVo+JWRFWozMEp18Izcb++H+A= 110 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= 111 | github.com/getlantern/ops v0.0.0-20220713155959-1315d978fff7/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= 112 | github.com/getlantern/ops v0.0.0-20230519221840-1283e026181c h1:qcPAzA1ZDnwx618jAgQmxo6UvJkw2SkM1L4ofncmEhI= 113 | github.com/getlantern/ops v0.0.0-20230519221840-1283e026181c/go.mod h1:g2ueCncOwWenlAr56Fh90FwsACkelqqtFUDLAHg1mng= 114 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 115 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 116 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 117 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 118 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 119 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 120 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 121 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= 122 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 123 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 124 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 125 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 126 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 127 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 128 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 129 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 130 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= 131 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= 132 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 h1:VkKnvzbvHqgEfm351rfr8Uclu5fnwq8HP2ximUzJsBM= 133 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8/go.mod h1:h29xCucjNsDcYb7+0rJokxVwYAq+9kQ19WiFuBKkYtc= 134 | github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a h1:VjN8ttdfklC0dnAdKbZqGNESdERUxtE3l8a/4Grgarc= 135 | github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k= 136 | github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI= 137 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 138 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 139 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 140 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 141 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 142 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 143 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 144 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 145 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 146 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 147 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 148 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 149 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 150 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 151 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 152 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 153 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 154 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 155 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 156 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 157 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 158 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 159 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 160 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 161 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 162 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 163 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 164 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 165 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 166 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 167 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 168 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 169 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 170 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 171 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 172 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 173 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 174 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 175 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 176 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 177 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 178 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 179 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 180 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 181 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 182 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 183 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 184 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 185 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 186 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 187 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 188 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 189 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 190 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 191 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 192 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 193 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 194 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 195 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 196 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 197 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 198 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 199 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 200 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 201 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 202 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 203 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 204 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 205 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 206 | github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI= 207 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 208 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 209 | github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= 210 | github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY= 211 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 212 | github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= 213 | github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= 214 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 215 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 216 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 217 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 218 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 219 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 220 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 221 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 222 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 223 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 224 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 225 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 226 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 227 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 228 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 229 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 230 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 231 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 232 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 233 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 234 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 235 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 236 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 237 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 238 | github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= 239 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 240 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 241 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 242 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 243 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk= 244 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= 245 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 246 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 247 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 248 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 249 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 250 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 251 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 252 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 253 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 254 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 255 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 256 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 257 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 258 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 259 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 260 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 261 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 262 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 263 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 264 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 265 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 266 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 267 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 268 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 269 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 270 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 271 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 272 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 273 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 274 | github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 275 | github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ= 276 | github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= 277 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= 278 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= 279 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 280 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 281 | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= 282 | github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 283 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 284 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 285 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 286 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 287 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 288 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 289 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 290 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 291 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 292 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 293 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 294 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 295 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 296 | github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= 297 | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= 298 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= 299 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 300 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 301 | github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 302 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 303 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 304 | github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 305 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 306 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 307 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 308 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 309 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 310 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 311 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 312 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 313 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 314 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 315 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 316 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 317 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 318 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 319 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 320 | github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= 321 | github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 322 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= 323 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= 324 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= 325 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= 326 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 327 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 328 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 329 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 330 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 331 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 332 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 333 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 334 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 335 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 336 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 337 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 338 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 339 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 340 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 341 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 342 | github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA= 343 | github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= 344 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 345 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 346 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 347 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 348 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 349 | github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 350 | github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU= 351 | github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 352 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 353 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 354 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 355 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 356 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 357 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 358 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 359 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 360 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 361 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 362 | go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= 363 | go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= 364 | go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= 365 | go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= 366 | go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= 367 | go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= 368 | go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= 369 | go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= 370 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 371 | go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 372 | go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 373 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 374 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 375 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 376 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 377 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 378 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 379 | go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 380 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 381 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 382 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 383 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 384 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 385 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 386 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 387 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 388 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 389 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 390 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 391 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 392 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 393 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 394 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 395 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 396 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 397 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 398 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 399 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 400 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 401 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= 402 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 403 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 404 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 405 | golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= 406 | golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= 407 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 408 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 409 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 410 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 411 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 412 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 413 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 414 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 415 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 416 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 417 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 418 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 419 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 420 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 421 | golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= 422 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg= 423 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc= 424 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 425 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 426 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 427 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 428 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 429 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 430 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 431 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 432 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 433 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 434 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 435 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 436 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 437 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 438 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 439 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 440 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 441 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 442 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 443 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 444 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 445 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 446 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 447 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 448 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 449 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 450 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 451 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 452 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 453 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 454 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 455 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 456 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 457 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 458 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 459 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 460 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 461 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 462 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 463 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 464 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 465 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 466 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 467 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 468 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 469 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 470 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 471 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 472 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 473 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 474 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 475 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 476 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 477 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 478 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 479 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 480 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 481 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 482 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 483 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 484 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 485 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 486 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 487 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 488 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 489 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 490 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 491 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 492 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 493 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 494 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 495 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 496 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 497 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 498 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 499 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 500 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 501 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 502 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 503 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 504 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 505 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 506 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 507 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 508 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 509 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 510 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 511 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 512 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 513 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 514 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 515 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 516 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 517 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 521 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 537 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 538 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 539 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 540 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 541 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 542 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 543 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 544 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 545 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 546 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 547 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 548 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 549 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 550 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 551 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 552 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 553 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 554 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 555 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 556 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 557 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 558 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 559 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 560 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 561 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 562 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 563 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 564 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 565 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 566 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 567 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 568 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 569 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 570 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 571 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 572 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 573 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 574 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 575 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 576 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 577 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 578 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 579 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 580 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 581 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 582 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 583 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 584 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 585 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 586 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 587 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 588 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 589 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 590 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 591 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 592 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 593 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 594 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 595 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 596 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 597 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 598 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 599 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 600 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 601 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 602 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 603 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 604 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 605 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 606 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 607 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 608 | golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= 609 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 610 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 611 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 612 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 613 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 614 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 615 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 616 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 617 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 618 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 619 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 620 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 621 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 622 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 623 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 624 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 625 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 626 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 627 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 628 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 629 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 630 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 631 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 632 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 633 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 634 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 635 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 636 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 637 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 638 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 639 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 640 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 641 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 642 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 643 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 644 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 645 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 646 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 647 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 648 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 649 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 650 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 651 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 652 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 653 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 654 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 655 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 656 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 657 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 658 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 659 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 660 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 661 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 662 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 663 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 664 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 665 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 666 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 667 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 668 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 669 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 670 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 671 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 672 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 673 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 674 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 675 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 676 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 677 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 678 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 679 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 680 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 681 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 682 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 683 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 684 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 685 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 686 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 687 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 688 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 689 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 690 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 691 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 692 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 693 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 694 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 695 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 696 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 697 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 698 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 699 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 700 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 701 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 702 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 703 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 704 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 705 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 706 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 707 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 708 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 709 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 710 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 711 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 712 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 713 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 714 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 715 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 716 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 717 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 718 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 719 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 720 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 721 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 722 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 723 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 724 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 725 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 726 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 727 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 728 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 729 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 730 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 731 | honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4= 732 | honnef.co/go/js/dom v0.0.0-20231112215516-51f43a291193 h1:BST3Y7yWfYPO8qqWhL2KtuqgRCbls6bIr4b4iFCg43I= 733 | honnef.co/go/js/dom v0.0.0-20231112215516-51f43a291193/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4= 734 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 735 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 736 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 737 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 738 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 739 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 740 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 741 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 742 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 743 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 744 | -------------------------------------------------------------------------------- /gui.go: -------------------------------------------------------------------------------- 1 | //go:build !no_gui 2 | // +build !no_gui 3 | 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "image/color" 10 | "io" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "strconv" 15 | "strings" 16 | "time" 17 | 18 | "fyne.io/fyne/v2" 19 | "fyne.io/fyne/v2/app" 20 | "fyne.io/fyne/v2/canvas" 21 | "fyne.io/fyne/v2/container" 22 | "fyne.io/fyne/v2/data/binding" 23 | "fyne.io/fyne/v2/dialog" 24 | "fyne.io/fyne/v2/driver/desktop" 25 | "fyne.io/fyne/v2/layout" 26 | "fyne.io/fyne/v2/widget" 27 | "github.com/nicksnyder/go-i18n/v2/i18n" 28 | ) 29 | 30 | var mainWindow fyne.Window 31 | 32 | var _fileLog *FetchLog 33 | 34 | func bootGui() { 35 | logFile, err := os.OpenFile(AppExecDir()+"/fetch.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) 36 | if err != nil { 37 | _cliLog.Print(t(&i18n.Message{ 38 | ID: "LogCreatedFail", 39 | Other: "日志文件创建失败", 40 | })) 41 | return 42 | } 43 | _fileLog = &FetchLog{w: logFile} 44 | logoResource := getLogoResource() 45 | a := app.New() 46 | a.Settings().SetTheme(&fghGuiTheme{}) 47 | 48 | mainWindow = a.NewWindow(fmt.Sprintf("Fetch Github Hosts - V%.1f", VERSION)) 49 | mainWindow.Resize(fyne.NewSize(800, 580)) 50 | mainWindow.SetIcon(logoResource) 51 | 52 | logoImage := canvas.NewImageFromResource(logoResource) 53 | logoImage.SetMinSize(fyne.NewSize(240, 240)) 54 | tabs := container.NewAppTabs( 55 | container.NewTabItem(t(&i18n.Message{ 56 | ID: "ClientMode", 57 | Other: "客户端模式", 58 | }), guiClientMode()), 59 | container.NewTabItem(t(&i18n.Message{ 60 | ID: "ServerMode", 61 | Other: "服务端模式", 62 | }), guiServerMode()), 63 | container.NewTabItem(t(&i18n.Message{ 64 | ID: "About", 65 | Other: "关于", 66 | }), container.NewVBox( 67 | newMargin(fyne.NewSize(10, 10)), 68 | container.New(layout.NewCenterLayout(), logoImage), 69 | guiAbout(), 70 | )), 71 | ) 72 | 73 | mainWindow.SetCloseIntercept(func() { 74 | mainWindow.Hide() 75 | }) 76 | 77 | mainWindow.CenterOnScreen() 78 | mainWindow.SetContent(container.NewVBox(tabs)) 79 | 80 | if err := GetCheckPermissionResult(); err != nil { 81 | time.AfterFunc(time.Second, func() { 82 | showAlert(err.Error()) 83 | }) 84 | } 85 | 86 | go checkVersion(nil) 87 | 88 | trayMenu := fyne.NewMenu("TrayMenu", fyne.NewMenuItem(t(&i18n.Message{ 89 | ID: "OpenHome", 90 | Other: "打开主界面", 91 | }), func() { 92 | mainWindow.Show() 93 | })) 94 | 95 | if desk, ok := a.(desktop.App); ok { 96 | desk.SetSystemTrayMenu(trayMenu) 97 | desk.SetSystemTrayIcon(logoResource) 98 | } 99 | 100 | mainWindow.ShowAndRun() 101 | } 102 | 103 | func getLogoResource() fyne.Resource { 104 | content, err := assetsFs.ReadFile("assets/public/logo.png") 105 | if err != nil { 106 | return nil 107 | } 108 | return &fyne.StaticResource{StaticName: "logo", StaticContent: content} 109 | } 110 | 111 | func getTicker(interval int) *time.Ticker { 112 | d := time.Minute 113 | if IsDebug() { 114 | d = time.Second 115 | } 116 | return time.NewTicker(d * time.Duration(interval)) 117 | } 118 | 119 | func guiClientMode() (content fyne.CanvasObject) { 120 | logs, addFn := newLogScrollComponent(fyne.NewSize(800, 280)) 121 | var cLog = NewFetchLog(NewGuiLogWriter(addFn)) 122 | var startBtn, stopBtn *widget.Button 123 | var interval, customUrl, selectUrl = strconv.Itoa(_conf.Client.Interval), _conf.Client.CustomUrl, _conf.Client.SelectOrigin 124 | var isCustomOrigin bool 125 | intervalInput, urlInput := widget.NewEntryWithData(binding.BindString(&interval)), widget.NewEntryWithData(binding.BindString(&customUrl)) 126 | var ticker *FetchTicker 127 | 128 | originSelectOpts := make([]string, 0, len(HostsOrigins)) 129 | for k := range HostsOrigins { 130 | originSelectOpts = append(originSelectOpts, k) 131 | } 132 | 133 | originMethodOpts := []string{ 134 | t(&i18n.Message{ 135 | ID: "HostsOptOfficial", 136 | Other: "官方指定hosts源", 137 | }), 138 | t(&i18n.Message{ 139 | ID: "HostsOptCustom", 140 | Other: "自定义hosts源", 141 | }), 142 | } 143 | 144 | originSelect := widget.NewSelect(originSelectOpts, func(s string) { 145 | _conf.Client.SelectOrigin = s 146 | selectUrl = HostsOrigins[s] 147 | }) 148 | originSelect.Selected = _conf.Client.SelectOrigin 149 | selectUrl = HostsOrigins[originSelect.Selected] 150 | 151 | intervalForm := widget.NewFormItem(t(&i18n.Message{ 152 | ID: "GetIntervalMinutes", 153 | Other: "获取间隔(分钟)", 154 | }), intervalInput) 155 | originSelectForm := widget.NewForm(widget.NewFormItem(t(&i18n.Message{ 156 | ID: "HostsOrigin", 157 | Other: "Hosts源", 158 | }), originSelect)) 159 | originCustomForm := widget.NewForm(widget.NewFormItem(t(&i18n.Message{ 160 | ID: "RemoteHostsUrl", 161 | Other: "远程Hosts链接", 162 | }), urlInput)) 163 | 164 | if _conf.Client.Method == originMethodOpts[0] { 165 | originCustomForm.Hide() 166 | } else { 167 | originSelectForm.Hide() 168 | } 169 | 170 | var form *widget.Form 171 | originMethod := widget.NewRadioGroup(originMethodOpts, nil) 172 | originMethodForm := widget.NewFormItem(t(&i18n.Message{ 173 | ID: "HostsOrigin", 174 | Other: "Hosts源", 175 | }), originMethod) 176 | originMethod.OnChanged = func(s string) { 177 | _conf.Client.Method = s 178 | if s == originMethodOpts[0] { 179 | originCustomForm.Hide() 180 | originSelectForm.Show() 181 | isCustomOrigin = false 182 | } else { 183 | originSelectForm.Hide() 184 | originCustomForm.Show() 185 | isCustomOrigin = true 186 | } 187 | } 188 | 189 | originMethod.Selected = _conf.Client.Method 190 | 191 | form = widget.NewForm( 192 | intervalForm, 193 | originMethodForm, 194 | ) 195 | 196 | startFetchExec := func() { 197 | intervalInt := parseStrIsNumberNotShowAlert(&interval, t(&i18n.Message{ 198 | ID: "GetIntervalNeedInt", 199 | Other: "获取间隔必须为整数", 200 | })) 201 | if intervalInt == nil { 202 | return 203 | } 204 | stopBtn.Enable() 205 | componentsStatusChange(false, startBtn, intervalInput, urlInput, originMethod, originSelect) 206 | ticker = NewFetchTicker(*intervalInt) 207 | if isCustomOrigin { 208 | go startClient(ticker, customUrl, cLog) 209 | } else { 210 | go startClient(ticker, selectUrl, cLog) 211 | } 212 | _conf.Client.CustomUrl = customUrl 213 | _conf.Client.Interval = *intervalInt 214 | _conf.Storage() 215 | } 216 | 217 | startBtn = widget.NewButton(t(&i18n.Message{ 218 | ID: "Start", 219 | Other: "启动", 220 | }), startFetchExec) 221 | stopBtn = widget.NewButton(t(&i18n.Message{ 222 | ID: "Stop", 223 | Other: "停止", 224 | }), func() { 225 | stopBtn.Disable() 226 | componentsStatusChange(true, startBtn, intervalInput, urlInput, originMethod, originSelect) 227 | ticker.Stop() 228 | }) 229 | 230 | if _conf.Client.AutoFetch { 231 | startFetchExec() 232 | startBtn.Disable() 233 | } else { 234 | stopBtn.Disable() 235 | } 236 | autoFetchCheck := widget.NewCheck(t(&i18n.Message{ 237 | ID: "StartupAutoGet", 238 | Other: "启动软件自动获取", 239 | }), func(b bool) { 240 | if b != _conf.Client.AutoFetch { 241 | _conf.Client.AutoFetch = b 242 | _conf.Storage() 243 | showAlert(t(&i18n.Message{ 244 | ID: "StartupAutoGetTips", 245 | Other: "启动软件自动获取状态已改变,将会在下次启动程序时生效!", 246 | })) 247 | } 248 | }) 249 | autoFetchCheck.SetChecked(_conf.Client.AutoFetch) 250 | 251 | buttons := container.New(layout.NewGridLayout(4), startBtn, stopBtn, widget.NewButton(t(&i18n.Message{ 252 | ID: "ClearHosts", 253 | Other: "清除hosts", 254 | }), func() { 255 | if err := flushCleanGithubHosts(); err != nil { 256 | showAlert(fmt.Sprintf("%s: %s", t(&i18n.Message{ 257 | ID: "CleanGithubHostsFail", 258 | Other: "清除hosts中的github记录失败", 259 | }), err.Error())) 260 | } else { 261 | showAlert(t(&i18n.Message{ 262 | ID: "CleanGithubHostsSuccess", 263 | Other: "hosts文件中的github记录已经清除成功", 264 | })) 265 | } 266 | }), container.New(layout.NewCenterLayout(), autoFetchCheck)) 267 | margin := newMargin(fyne.NewSize(10, 10)) 268 | return container.NewVBox(margin, form, originSelectForm, originCustomForm, margin, buttons, 269 | margin, logs) 270 | } 271 | 272 | func guiServerMode() (content fyne.CanvasObject) { 273 | logs, addFn := newLogScrollComponent(fyne.NewSize(800, 320)) 274 | var sLog = NewFetchLog(NewGuiLogWriter(addFn)) 275 | var startBtn, stopBtn *widget.Button 276 | var interval, port = strconv.Itoa(_conf.Server.Interval), strconv.Itoa(_conf.Server.Port) 277 | var ticker *FetchTicker 278 | intervalInput, portInput := widget.NewEntryWithData(binding.BindString(&interval)), widget.NewEntryWithData(binding.BindString(&port)) 279 | statusLabel := widget.NewHyperlink(t(&i18n.Message{ 280 | ID: "ListeningAddressWait", 281 | Other: "监听地址:待启动", 282 | }), nil) 283 | startBtn = widget.NewButton(t(&i18n.Message{ 284 | ID: "Start", 285 | Other: "启动", 286 | }), func() { 287 | portInt := parseStrIsNumberNotShowAlert(&port, t(&i18n.Message{ 288 | ID: "PortMustBeInt", 289 | Other: "端口号必须为整数", 290 | })) 291 | if portInt == nil { 292 | return 293 | } 294 | intervalInt := parseStrIsNumberNotShowAlert(&interval, t(&i18n.Message{ 295 | ID: "GetIntervalNeedInt", 296 | Other: "获取间隔必须为整数", 297 | })) 298 | if intervalInt == nil { 299 | return 300 | } 301 | stopBtn.Enable() 302 | componentsStatusChange(false, startBtn, intervalInput, portInput) 303 | ticker = NewFetchTicker(*intervalInt) 304 | go startServer(ticker, *portInt, sLog) 305 | _conf.Server.Interval = *intervalInt 306 | _conf.Server.Port = *portInt 307 | _conf.Storage() 308 | listenerUrl := fmt.Sprintf("http://127.0.0.1:%d", *portInt) 309 | statusLabel.SetText(tf(&i18n.LocalizeConfig{ 310 | DefaultMessage: &i18n.Message{ 311 | ID: "ListeningAddress", 312 | Other: "监听地址 {{.Addr}}", 313 | }, 314 | TemplateData: map[string]interface{}{ 315 | "Addr": listenerUrl, 316 | }, 317 | })) 318 | statusLabel.SetURLFromString(listenerUrl) 319 | }) 320 | stopBtn = widget.NewButton(t(&i18n.Message{ 321 | ID: "Stop", 322 | Other: "停止", 323 | }), func() { 324 | stopBtn.Disable() 325 | componentsStatusChange(true, startBtn, intervalInput, portInput) 326 | statusLabel.SetText(t(&i18n.Message{ 327 | ID: "ListeningAddressWait", 328 | Other: "监听地址:待启动", 329 | })) 330 | ticker.Stop() 331 | }) 332 | stopBtn.Disable() 333 | form := widget.NewForm( 334 | widget.NewFormItem(t(&i18n.Message{ 335 | ID: "GetIntervalMinutes", 336 | Other: "获取间隔(分钟)", 337 | }), intervalInput), 338 | widget.NewFormItem(t(&i18n.Message{ 339 | ID: "StartupPort", 340 | Other: "启动端口号", 341 | }), portInput), 342 | ) 343 | buttons := container.New(layout.NewGridLayout(2), startBtn, stopBtn) 344 | return container.NewVBox(newMargin(fyne.NewSize(10, 10)), form, buttons, 345 | container.New(layout.NewCenterLayout(), statusLabel), 346 | logs, 347 | ) 348 | } 349 | 350 | func guiAbout() (content fyne.CanvasObject) { 351 | aboutNote := widget.NewRichTextFromMarkdown(fmt.Sprintf(` 352 | %s 353 | %s 354 | `, t(&i18n.Message{ 355 | ID: "AboutContent", 356 | Other: `# 介绍 357 | Fetch Github Hosts是主要为解决研究及学习人员访问Github过慢或其他问题而提供的Github Hosts同步工具 358 | --- 359 | # 开源协议 360 | GNU General Public License v3.0 361 | 362 | # 版本号`, 363 | }), fmt.Sprintf("V%.1f", VERSION))) 364 | for i := range aboutNote.Segments { 365 | if seg, ok := aboutNote.Segments[i].(*widget.TextSegment); ok { 366 | seg.Style.Alignment = fyne.TextAlignCenter 367 | } 368 | if seg, ok := aboutNote.Segments[i].(*widget.HyperlinkSegment); ok { 369 | seg.Alignment = fyne.TextAlignCenter 370 | } 371 | } 372 | languages := map[string]string{ 373 | "简体中文": "zh-CN", 374 | "English": "en-US", 375 | } 376 | langSelectOpts := make([]string, 0, len(languages)) 377 | currentLang := "简体中文" 378 | for k := range languages { 379 | langSelectOpts = append(langSelectOpts, k) 380 | if languages[k] == _conf.Lang { 381 | currentLang = k 382 | } 383 | } 384 | originSelect := widget.NewSelect(langSelectOpts, func(s string) { 385 | _conf.Lang = languages[s] 386 | _conf.Storage() 387 | showAlert(tfs(&i18n.Message{ 388 | ID: "LangChangeTips", 389 | Other: "语言已经切换为 {{.Lang}},将会在下次启动程序时生效!", 390 | }, map[string]interface{}{ 391 | "Lang": s, 392 | })) 393 | }) 394 | originSelect.Selected = currentLang 395 | github := widget.NewButton("Github", openUrl("https://github.com/Licoy/fetch-github-hosts")) 396 | feedback := widget.NewButton(t(&i18n.Message{ 397 | ID: "Feedback", 398 | Other: "反馈建议", 399 | }), openUrl("https://github.com/Licoy/fetch-github-hosts/issues")) 400 | var cv *widget.Button 401 | cv = widget.NewButton(t(&i18n.Message{ 402 | ID: "CheckUpdate", 403 | Other: "检查更新", 404 | }), func() { 405 | checkVersion(cv) 406 | }) 407 | return container.NewVBox(aboutNote, container.New(layout.NewCenterLayout(), container.NewHBox(originSelect, github, feedback, cv))) 408 | } 409 | 410 | func checkVersion(btn *widget.Button) { 411 | if btn != nil { 412 | btn.Disable() 413 | defer btn.Enable() 414 | } 415 | alertHandler := func(msg string) { 416 | if btn != nil { 417 | showAlert(msg) 418 | } 419 | } 420 | resp, err := http.Get("https://api.github.com/repos/Licoy/fetch-github-hosts/releases") 421 | if err != nil { 422 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 423 | ID: "NetworkRequestFail", 424 | Other: "网络请求错误", 425 | }), err.Error())) 426 | return 427 | } 428 | if resp.StatusCode != http.StatusOK { 429 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 430 | ID: "RequestFail", 431 | Other: "请求失败", 432 | }), err.Error())) 433 | return 434 | } 435 | all, err := io.ReadAll(resp.Body) 436 | if err != nil { 437 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 438 | ID: "ReadUpdateResponseFail", 439 | Other: "读取更新响应内容失败", 440 | }), err.Error())) 441 | return 442 | } 443 | var releases []struct { 444 | TagName string `json:"tag_name"` 445 | HtmlUrl string `json:"html_url"` 446 | } 447 | if err = json.Unmarshal(all, &releases); err != nil { 448 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 449 | ID: "ParseUpdateResponseFail", 450 | Other: "解析更新响应内容失败", 451 | }), err.Error())) 452 | return 453 | } 454 | if len(releases) == 0 { 455 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 456 | ID: "CheckUpdateFail", 457 | Other: "检查更新失败", 458 | }), err.Error())) 459 | return 460 | } 461 | verStr := strings.Replace(strings.Replace(releases[0].TagName, "v", "", 1), "V", "", 1) 462 | float, err := strconv.ParseFloat(verStr, 64) 463 | if err != nil { 464 | alertHandler(fmt.Sprintf("%s: %s", t(&i18n.Message{ 465 | ID: "VersionParseFail", 466 | Other: "版本号解析失败", 467 | }), err.Error())) 468 | return 469 | } 470 | if VERSION >= float { 471 | alertHandler(t(&i18n.Message{ 472 | ID: "CurrentIsNewest", 473 | Other: "当前已是最新版本", 474 | })) 475 | return 476 | } 477 | confirm := dialog.NewConfirm(t(&i18n.Message{ 478 | ID: "UpdateTip", 479 | Other: "更新提示", 480 | }), t(&i18n.Message{ 481 | ID: "UpdateTipContent", 482 | Other: "检测到有新的版本,是否立即需要去下载最新版本?", 483 | }), func(b bool) { 484 | if b { 485 | openUrl(releases[0].HtmlUrl)() 486 | } 487 | }, mainWindow) 488 | confirm.SetDismissText(t(&i18n.Message{ 489 | ID: "UpdateLater", 490 | Other: "稍后更新", 491 | })) 492 | confirm.SetConfirmText(t(&i18n.Message{ 493 | ID: "DownloadNow", 494 | Other: "立即下载", 495 | })) 496 | confirm.Show() 497 | } 498 | 499 | func showAlert(msg string) { 500 | dialog.NewCustom(t(&i18n.Message{ 501 | ID: "Tip", 502 | Other: "提示", 503 | }), t(&i18n.Message{ 504 | ID: "Ok", 505 | Other: "确认", 506 | }), widget.NewLabel(msg), mainWindow).Show() 507 | } 508 | 509 | func parseStrIsNumberNotShowAlert(str *string, msg string) *int { 510 | res, err := strconv.Atoi(*str) 511 | if err != nil { 512 | showAlert(msg) 513 | return nil 514 | } 515 | return &res 516 | } 517 | 518 | func newLogScrollComponent(size fyne.Size) (scroll *container.Scroll, addFn func(string)) { 519 | var logs string 520 | textarea := widget.NewMultiLineEntry() 521 | textarea.Wrapping = fyne.TextWrapBreak 522 | textarea.Disable() 523 | scroll = container.NewScroll(textarea) 524 | scroll.SetMinSize(size) 525 | addFn = func(s string) { 526 | logs = s + logs 527 | textarea.SetText(logs) 528 | } 529 | return 530 | } 531 | 532 | func componentsStatusChange(enable bool, components ...fyne.Disableable) { 533 | for _, v := range components { 534 | if enable { 535 | v.Enable() 536 | } else { 537 | v.Disable() 538 | } 539 | } 540 | } 541 | 542 | func openUrl(urlStr string) func() { 543 | return func() { 544 | u, _ := url.Parse(urlStr) 545 | _ = fyne.CurrentApp().OpenURL(u) 546 | } 547 | } 548 | 549 | func newMargin(size fyne.Size) *canvas.Rectangle { 550 | margin := canvas.NewRectangle(color.Transparent) 551 | margin.SetMinSize(size) 552 | return margin 553 | } 554 | -------------------------------------------------------------------------------- /gui_theme.go: -------------------------------------------------------------------------------- 1 | //go:build !no_gui 2 | // +build !no_gui 3 | 4 | package main 5 | 6 | import ( 7 | "image/color" 8 | 9 | "fyne.io/fyne/v2" 10 | "fyne.io/fyne/v2/theme" 11 | ) 12 | 13 | type fghGuiTheme struct { 14 | } 15 | 16 | func (f *fghGuiTheme) Font(s fyne.TextStyle) fyne.Resource { 17 | font, err := assetsFs.ReadFile("assets/" + GuiFontName) 18 | if err != nil { 19 | return theme.DefaultTheme().Font(s) 20 | } 21 | return fyne.NewStaticResource("fgh-font", font) 22 | } 23 | 24 | func (*fghGuiTheme) Color(c fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { 25 | switch c { 26 | case theme.ColorNamePrimary, theme.ColorNameButton: 27 | //#009966 28 | return color.RGBA{G: 0x99, B: 0x66, A: 0xff} 29 | case theme.ColorNameBackground: 30 | //#191b2c 31 | return color.RGBA{R: 0x19, G: 0x1b, B: 0x2c, A: 0xff} 32 | case theme.ColorNameMenuBackground, theme.ColorNameInputBackground, theme.ColorNameOverlayBackground: 33 | //#1f2437 34 | return color.RGBA{R: 0x1f, G: 0x24, B: 0x37, A: 0xff} 35 | case theme.ColorNameDisabledButton: 36 | //#629181 37 | return color.RGBA{R: 0x62, G: 0x91, B: 0x81, A: 0xff} 38 | case theme.ColorNameDisabled: 39 | //#34364a 40 | return color.RGBA{R: 0x34, G: 0x36, B: 0x4a, A: 0xff} 41 | default: 42 | return theme.DefaultTheme().Color(c, theme.VariantDark) 43 | } 44 | } 45 | 46 | func (*fghGuiTheme) Icon(n fyne.ThemeIconName) fyne.Resource { 47 | return theme.DefaultTheme().Icon(n) 48 | } 49 | 50 | func (*fghGuiTheme) Size(n fyne.ThemeSizeName) float32 { 51 | return theme.DefaultTheme().Size(n) 52 | } 53 | -------------------------------------------------------------------------------- /i18n.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nicksnyder/go-i18n/v2/i18n" 6 | ) 7 | 8 | func tf(cfg *i18n.LocalizeConfig) string { 9 | localize, err := _local.Localize(cfg) 10 | if err != nil { 11 | fmt.Println(err) 12 | if cfg.DefaultMessage.One != "" { 13 | return cfg.DefaultMessage.One 14 | } 15 | return cfg.DefaultMessage.Other 16 | } 17 | return localize 18 | } 19 | 20 | func tfs(msg *i18n.Message, params map[string]interface{}) string { 21 | return tf(&i18n.LocalizeConfig{ 22 | DefaultMessage: msg, 23 | TemplateData: params, 24 | }) 25 | } 26 | 27 | func t(msg *i18n.Message) string { 28 | return tf(&i18n.LocalizeConfig{ 29 | DefaultMessage: msg, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | ) 8 | 9 | type FetchLog struct { 10 | w io.Writer 11 | } 12 | 13 | func NewFetchLog(w io.Writer) *FetchLog { 14 | return &FetchLog{w} 15 | } 16 | 17 | func (f *FetchLog) Print(msg string) { 18 | now := time.Now().Format("2006-01-02 15:04:05") 19 | fmt.Fprintf(f.w, "[%s] %s\n", now, msg) 20 | } 21 | 22 | type guiLogWriter struct { 23 | addFn func(string) 24 | } 25 | 26 | func NewGuiLogWriter(addFn func(string)) io.Writer { 27 | return &guiLogWriter{addFn} 28 | } 29 | 30 | func (g *guiLogWriter) Write(p []byte) (n int, err error) { 31 | g.addFn(string(p)) 32 | return len(p), nil 33 | } 34 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "github.com/getlantern/elevate" 7 | "github.com/nicksnyder/go-i18n/v2/i18n" 8 | "github.com/pelletier/go-toml/v2" 9 | "golang.org/x/text/language" 10 | "os" 11 | "runtime" 12 | ) 13 | 14 | //go:embed assets 15 | var assetsFs embed.FS 16 | 17 | //go:embed active.*.toml 18 | var localeFS embed.FS 19 | 20 | var _cliLog = &FetchLog{w: os.Stdout} 21 | var _local *i18n.Localizer 22 | var _conf *FetchConf 23 | 24 | func main() { 25 | args := ParseBootArgs() 26 | if !args.DontEscalate && !args.Escalate && runtime.GOOS != Linux { 27 | cmd := elevate.Command(os.Args[0], "--escalate") 28 | cmd.Run() 29 | os.Exit(0) 30 | } 31 | isGui := args.Mode == "" 32 | bundle := i18n.NewBundle(language.Chinese) 33 | bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) 34 | bundle.LoadMessageFileFS(localeFS, "active.en-US.toml") 35 | _conf = LoadFetchConf() 36 | _local = i18n.NewLocalizer(bundle, args.Lang, _conf.Lang) 37 | if isGui { 38 | bootGui() 39 | } else { 40 | bootCli(args) 41 | } 42 | } 43 | 44 | func bootCli(args *CmdArgs) { 45 | if !IsDebug() { 46 | if err := GetCheckPermissionResult(); err != nil { 47 | _cliLog.Print(err.Error()) 48 | return 49 | } 50 | } 51 | _cliLog.Print(fmt.Sprintf("开始程序监听,当前以%d分钟更新一次Github-Hosts!", args.FetchInterval)) 52 | _cliLog.Print("请不要关闭此窗口以保持再前台运行") 53 | _cliLog.Print("可以将此程序注册为服务,具体请参考项目说明:https://github.com/Licoy/fetch-github-hosts") 54 | 55 | ticker := NewFetchTicker(args.FetchInterval) 56 | if args.Mode == "server" { 57 | startServer(ticker, args.Port, _cliLog) 58 | } else { 59 | startClient(ticker, args.Url, _cliLog) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /no_gui.go: -------------------------------------------------------------------------------- 1 | //go:build no_gui 2 | // +build no_gui 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | var _fileLog *FetchLog 12 | 13 | func bootGui() { 14 | fmt.Println("GUI is not supported in this build") 15 | fmt.Println("Please use the command line interface") 16 | fmt.Println("For example: sudo ./fetch-github-hosts -m client ") 17 | } 18 | 19 | func getTicker(interval int) *time.Ticker { 20 | d := time.Minute 21 | if IsDebug() { 22 | d = time.Second 23 | } 24 | return time.NewTicker(d * time.Duration(interval)) 25 | } 26 | -------------------------------------------------------------------------------- /ticker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | type FetchTicker struct { 6 | Ticker *time.Ticker 7 | CloseChan chan struct{} 8 | } 9 | 10 | func NewFetchTicker(interval int) *FetchTicker { 11 | return &FetchTicker{getTicker(interval), make(chan struct{})} 12 | } 13 | 14 | func (f *FetchTicker) Stop() { 15 | f.Ticker.Stop() 16 | close(f.CloseChan) 17 | } 18 | -------------------------------------------------------------------------------- /use.sh: -------------------------------------------------------------------------------- 1 | # i18n 2 | goi18n extract -sourceLanguage zh-CN 3 | goi18n merge -sourceLanguage zh-CN active.zh-CN.toml active.en-US.toml 4 | goi18n merge -sourceLanguage en-US translate.en-US.toml active.en-US.toml -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "errors" 6 | "fmt" 7 | "github.com/nicksnyder/go-i18n/v2/i18n" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | "syscall" 14 | ) 15 | 16 | const ( 17 | VERSION = 2.8 18 | ) 19 | 20 | var ( 21 | _debug bool 22 | _execDir string 23 | _logWrite io.Writer 24 | ) 25 | 26 | func init() { 27 | _logWrite = os.Stdout 28 | _debug = os.Getenv("FETCH_GITHUB_HOST_DEBUG") != "" 29 | initAppExecDir() 30 | } 31 | 32 | func initAppExecDir() { 33 | if _debug { 34 | _execDir, _ = os.Getwd() 35 | } else { 36 | _exec, _ := os.Executable() 37 | _execDir = filepath.Dir(_exec) 38 | } 39 | } 40 | 41 | func IsDebug() bool { 42 | return _debug 43 | } 44 | 45 | func AppExecDir() string { 46 | return _execDir 47 | } 48 | 49 | func GetSystemHostsPath() string { 50 | switch runtime.GOOS { 51 | case Windows: 52 | return os.Getenv("SystemRoot") + "\\System32\\drivers\\etc\\hosts" 53 | } 54 | return "/etc/hosts" 55 | } 56 | 57 | func PreCheckHasHostsRWPermission() (yes bool, err error) { 58 | h, err := syscall.Open(GetSystemHostsPath(), syscall.O_RDWR, 0655) 59 | if err != nil { 60 | if strings.Contains(err.Error(), "Access is denied") { 61 | err = nil 62 | } 63 | return 64 | } 65 | syscall.Close(h) 66 | yes = true 67 | return 68 | } 69 | 70 | func ComposeError(msg string, err error) error { 71 | if err == nil { 72 | return errors.New(msg) 73 | } 74 | return errors.New(msg + ": " + err.Error()) 75 | } 76 | 77 | func GetExecOrEmbedFile(fs *embed.FS, filename string) (template []byte, err error) { 78 | exeDirFile := AppExecDir() + "/" + filename 79 | _, err = os.Stat(exeDirFile) 80 | if err == nil { 81 | template, err = os.ReadFile(exeDirFile) 82 | return 83 | } 84 | template, err = fs.ReadFile(filename) 85 | return 86 | } 87 | 88 | func GetCheckPermissionResult() (err error) { 89 | permission, err := PreCheckHasHostsRWPermission() 90 | if err != nil { 91 | err = ComposeError(t(&i18n.Message{ 92 | ID: "PermissionCheckFail", 93 | Other: "检查hosts读写权限失败,请以sudo或管理员身份来运行本程序!", 94 | }), err) 95 | return 96 | } 97 | if !permission { 98 | if runtime.GOOS == Windows { 99 | err = errors.New(t(&i18n.Message{ 100 | ID: "RunAsAdminWin", 101 | Other: "请鼠标右键选择【以管理员的身份运行】来执行本程序!", 102 | })) 103 | fmt.Println(t(&i18n.Message{ 104 | ID: "RunAsAdminWin", 105 | Other: "请鼠标右键选择【以管理员的身份运行】来执行本程序!", 106 | })) 107 | } else { 108 | err = ComposeError(t(&i18n.Message{ 109 | ID: "RunAsAdminUnix", 110 | Other: "请以root账户或sudo来执行本程序!", 111 | }), err) 112 | } 113 | } 114 | return 115 | } 116 | 117 | func GetNewlineChar() string { 118 | if runtime.GOOS == Windows { 119 | return "\r\n" 120 | } 121 | return "\n" 122 | } 123 | --------------------------------------------------------------------------------