sync: Merge branch 'master' of https://github.com/mgkbadola/remark42 into discord-oauth

This commit is contained in:
Mrigank Badola
2024-11-10 10:43:10 +05:30
199 changed files with 3654 additions and 2087 deletions
+74
View File
@@ -0,0 +1,74 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
"GitHub Actions updates":
patterns:
- "*"
- package-ecosystem: "gomod"
directory: "/backend"
schedule:
interval: "monthly"
groups:
"Go modules updates":
dependency-type: "production"
- package-ecosystem: "gomod"
directory: "/backend/_example/memory_store"
schedule:
interval: "monthly"
groups:
"Go modules updates":
dependency-type: "production"
- package-ecosystem: "npm"
directory: "/frontend"
schedule:
interval: "monthly"
groups:
"NPM modules updates":
dependency-type: "production"
"NPM modules updates for tests":
dependency-type: "development"
- package-ecosystem: "npm"
directory: "/frontend/packages/api"
schedule:
interval: "monthly"
groups:
"NPM modules updates":
dependency-type: "production"
"NPM modules updates for tests":
dependency-type: "development"
- package-ecosystem: "npm"
directory: "/frontend/e2e"
schedule:
interval: "monthly"
groups:
"NPM modules updates":
dependency-type: "production"
"NPM modules updates for tests":
dependency-type: "development"
- package-ecosystem: "npm"
directory: "/frontend/apps/remark42"
schedule:
interval: "monthly"
groups:
"NPM modules updates":
dependency-type: "production"
"NPM modules updates for tests":
dependency-type: "development"
- package-ecosystem: "npm"
directory: "/site"
schedule:
interval: "monthly"
groups:
"NPM modules updates":
dependency-type: "production"
"NPM modules updates for tests":
dependency-type: "development"
+5 -5
View File
@@ -32,7 +32,7 @@ jobs:
- name: install go
uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: test and build backend
run: |
@@ -52,15 +52,15 @@ jobs:
TZ: "America/Chicago"
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: v1.58
version: v1.61.0
working-directory: backend/app
- name: golangci-lint on example directory
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: v1.58
version: v1.61.0
args: --config ../../.golangci.yml
working-directory: backend/_example/memory_store
+15 -13
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [ 16 ]
steps:
- name: Checkout
@@ -32,7 +32,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -44,7 +44,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -64,7 +64,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [ 16 ]
steps:
- name: Checkout
@@ -76,7 +76,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -88,7 +88,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -108,7 +108,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [ 16 ]
steps:
- name: Checkout
@@ -120,7 +120,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -132,7 +132,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -147,7 +147,9 @@ jobs:
run: pnpm coverage:api
working-directory: ./frontend
- name: Submit coverage
run: ${{ github.workspace }}/frontend/apps/remark42/node_modules/.bin/codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
working-directory: ./frontend
codecov_yml_path: ./frontend/apps/remark42/codecov.yml
+20 -18
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [16]
steps:
- name: Checkout
@@ -32,7 +32,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -44,7 +44,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -64,7 +64,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [16]
steps:
- name: Checkout
@@ -76,7 +76,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -88,7 +88,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -108,7 +108,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [16]
steps:
- name: Checkout
@@ -120,7 +120,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -132,7 +132,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -158,14 +158,14 @@ jobs:
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
run_install: false
- name: Check bundle size
uses: andresz1/size-limit-action@7313b26c76b3666c1dc41e2ca05370e201a9b7de
uses: andresz1/size-limit-action@94bc357df29c36c8f8d50ea497c3e225c3c95d1d
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
directory: ./frontend/apps/remark42
@@ -176,7 +176,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16.20.2]
node: [16]
steps:
- name: Checkout
@@ -188,7 +188,7 @@ jobs:
node-version: ${{ matrix.node }}
- name: Install pnpm
uses: pnpm/action-setup@v2.4.0
uses: pnpm/action-setup@v4.0.0
id: pnpm-install
with:
version: 7
@@ -200,7 +200,7 @@ jobs:
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -215,7 +215,9 @@ jobs:
run: pnpm coverage
working-directory: ./frontend/apps/remark42
- name: Submit coverage
run: ${{ github.workspace }}/frontend/apps/remark42/node_modules/.bin/codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
working-directory: ./frontend/apps/remark42
codecov_yml_path: ./frontend/apps/remark42/codecov.yml
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
id: tests
run: COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f compose-e2e-test.yml up --build --quiet-pull --exit-code-from tests
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
+2 -2
View File
@@ -45,7 +45,7 @@ RUN \
echo 'Skip frontend build'; \
fi
FROM umputun/baseimage:buildgo-v1.13.0 as build-backend
FROM umputun/baseimage:buildgo-v1.14.0 AS build-backend
ARG CI
ARG GITHUB_REF
@@ -81,7 +81,7 @@ RUN \
echo "version=$version" && \
go build -o remark42 -ldflags "-X main.revision=${version} -s -w" ./app
FROM umputun/baseimage:app-v1.13.0
FROM umputun/baseimage:app-v1.14.0
ARG GITHUB_SHA
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:16.20.2-alpine AS frontend-deps
FROM node:16-alpine AS frontend-deps
ENV CI=true
@@ -19,7 +19,7 @@ WORKDIR /srv/frontend/apps/remark42/
COPY ./frontend/apps/remark42/ /srv/frontend/apps/remark42/
RUN pnpm build
FROM umputun/baseimage:buildgo-v1.13.0 as build-backend
FROM umputun/baseimage:buildgo-v1.14.0 AS build-backend
ARG GITHUB_TOKEN
ARG GITHUB_REF
+4 -4
View File
@@ -39,15 +39,15 @@ race_test:
cd backend/app && go test -race -timeout=60s -count 1 ./...
backend:
docker-compose -f compose-dev-backend.yml build
docker compose -f compose-dev-backend.yml build
frontend:
docker-compose -f compose-dev-frontend.yml build
docker compose -f compose-dev-frontend.yml build
rundev:
SKIP_BACKEND_TEST=true SKIP_FRONTEND_TEST=true GITHUB_REF=$(GITHUB_REF) GITHUB_SHA=$(GITHUB_SHA) CI=true \
docker-compose -f compose-private.yml build
docker-compose -f compose-private.yml up
docker compose -f compose-private.yml build
docker compose -f compose-private.yml up
e2e:
docker compose -f compose-e2e-test.yml up --build --quiet-pull --exit-code-from tests
+1 -1
View File
@@ -2,7 +2,7 @@
Remark42 is a self-hosted, lightweight and simple (yet functional) comment engine, which doesn't spy on users. It can be embedded into blogs, articles, or any other place where readers add comments.
* Social login via Google, Twitter, Facebook, Microsoft, GitHub, Yandex, Patreon, Discord and Telegram
* Social login via Google, Facebook, Microsoft, GitHub, Apple, Yandex, Patreon, Discord and Telegram
* Login via email
* Optional anonymous access
* Multi-level nested comments with both tree and plain presentations
+1 -1
View File
@@ -26,8 +26,8 @@ linters-settings:
linters:
enable:
- bodyclose
- copyloopvar
- dupl
- exportloopref
- gochecknoinits
- gocritic
- gocyclo
+2 -2
View File
@@ -1,11 +1,11 @@
FROM umputun/baseimage:buildgo-v1.13.0 as build-backend
FROM umputun/baseimage:buildgo-v1.14.0 AS build-backend
ADD backend /build/backend
WORKDIR /build/backend/_example/memory_store
RUN go build -o /build/bin/memory_store -ldflags "-X main.revision=0.0.0 -s -w"
FROM umputun/baseimage:app-v1.13.0
FROM umputun/baseimage:app-v1.14.0
ARG GITHUB_SHA
+3 -3
View File
@@ -4,9 +4,9 @@
In order to run remark42 with memory_store copy provided `compose-dev-memstore.yml` to the root directory and run:
1. `docker-compose -f compose-dev-memstore.yml build`
1. `docker-compose -f compose-dev-memstore.yml up`
1. `docker compose -f compose-dev-memstore.yml build`
1. `docker compose -f compose-dev-memstore.yml up`
As usual, demo site will run on http://127.0.0.1:8080/web/
note: in order to work with the latest (current) version of master `go.mod` uses replacement directive for the backend package. In real-life usage `replace github.com/umputun/remark42/backend => ../../` should not be used.
note: in order to work with the latest (current) version of master `go.mod` uses replacement directive for the backend package. In real-life usage `replace github.com/umputun/remark42/backend => ../../` should not be used.
+8 -9
View File
@@ -1,6 +1,6 @@
module github.com/umputun/remark42/memory_store
go 1.21
go 1.23
require (
github.com/go-pkgz/jrpc v0.3.0
@@ -12,9 +12,9 @@ require (
require (
github.com/Depado/bfchroma/v2 v2.0.0 // indirect
github.com/PuerkitoBio/goquery v1.9.2 // indirect
github.com/PuerkitoBio/goquery v1.10.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/alecthomas/chroma/v2 v2.13.0 // indirect
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -30,13 +30,12 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
golang.org/x/image v0.22.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+19 -20
View File
@@ -1,13 +1,13 @@
github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8=
github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
@@ -63,8 +63,8 @@ github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -75,12 +75,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -88,13 +88,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -102,8 +102,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -119,9 +119,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-1
View File
@@ -46,7 +46,6 @@ func TestCleanup_IsSpam(t *testing.T) {
}
for n, tt := range tbl {
tt := tt
checkName := fmt.Sprintf("check-%d-%s", n, tt.name)
t.Run(checkName, func(t *testing.T) {
c := store.Comment{ID: checkName, Text: tt.text, Score: tt.score}
+9 -2
View File
@@ -85,7 +85,7 @@ type ServerCommand struct {
EnableEmoji bool `long:"emoji" env:"EMOJI" description:"enable emoji"`
SimpleView bool `long:"simple-view" env:"SIMPLE_VIEW" description:"minimal comment editor mode"`
ProxyCORS bool `long:"proxy-cors" env:"PROXY_CORS" description:"disable internal CORS and delegate it to proxy"`
AllowedHosts []string `long:"allowed-hosts" env:"ALLOWED_HOSTS" description:"limit hosts/sources allowed to embed comments" env-delim:","`
AllowedHosts []string `long:"allowed-hosts" env:"ALLOWED_HOSTS" description:"limit hosts/sources allowed to embed comments via CSP 'frame-ancestors''" env-delim:","`
SubscribersOnly bool `long:"subscribers-only" env:"SUBSCRIBERS_ONLY" description:"enable commenting only for Patreon subscribers"`
DisableSignature bool `long:"disable-signature" env:"DISABLE_SIGNATURE" description:"disable server signature in headers"`
DisableFancyTextFormatting bool `long:"disable-fancy-text-formatting" env:"DISABLE_FANCY_TEXT_FORMATTING" description:"disable fancy comments text formatting (replacement of quotes, dashes, fractions, etc)"`
@@ -105,7 +105,7 @@ type ServerCommand struct {
Facebook AuthGroup `group:"facebook" namespace:"facebook" env-namespace:"FACEBOOK" description:"Facebook OAuth"`
Microsoft AuthGroup `group:"microsoft" namespace:"microsoft" env-namespace:"MICROSOFT" description:"Microsoft OAuth"`
Yandex AuthGroup `group:"yandex" namespace:"yandex" env-namespace:"YANDEX" description:"Yandex OAuth"`
Twitter AuthGroup `group:"twitter" namespace:"twitter" env-namespace:"TWITTER" description:"Twitter OAuth"`
Twitter AuthGroup `group:"twitter" namespace:"twitter" env-namespace:"TWITTER" description:"[deprecated, doesn't work] Twitter OAuth"`
Patreon AuthGroup `group:"patreon" namespace:"patreon" env-namespace:"PATREON" description:"Patreon OAuth"`
Discord AuthGroup `group:"discord" namespace:"discord" env-namespace:"DISCORD" description:"Discord OAuth"`
Telegram bool `long:"telegram" env:"TELEGRAM" description:"Enable Telegram auth (using token from telegram.token)"`
@@ -314,6 +314,7 @@ func (s *ServerCommand) Execute(_ []string) error {
log.Printf("[INFO] start server on port %s:%d", s.Address, s.Port)
resetEnv(
"SECRET",
"AUTH_APPLE_KID",
"AUTH_GOOGLE_CSEC",
"AUTH_GITHUB_CSEC",
"AUTH_FACEBOOK_CSEC",
@@ -408,6 +409,12 @@ func (s *ServerCommand) HandleDeprecatedFlags() (result []DeprecatedFlag) {
if s.Notify.Telegram.API != "https://api.telegram.org/bot" {
result = append(result, DeprecatedFlag{Old: "notify.telegram.api", Version: "1.9"})
}
if s.Auth.Twitter.CID != "" {
result = append(result, DeprecatedFlag{Old: "auth.twitter.cid", Version: "1.14"})
}
if s.Auth.Twitter.CSEC != "" {
result = append(result, DeprecatedFlag{Old: "auth.twitter.csec", Version: "1.14"})
}
return append(result, s.findDeprecatedFlagsCollisions()...)
}
+4 -1
View File
@@ -457,6 +457,8 @@ func TestServerApp_DeprecatedArgs(t *testing.T) {
"--notify.telegram.token=abcd",
"--notify.telegram.timeout=3m",
"--notify.telegram.api=http://example.org",
"--auth.twitter.cid=123",
"--auth.twitter.csec=456",
}
assert.Empty(t, s.SMTP.Host)
assert.Empty(t, s.SMTP.Port)
@@ -482,6 +484,8 @@ func TestServerApp_DeprecatedArgs(t *testing.T) {
{Old: "notify.telegram.token", New: "telegram.token", Version: "1.9"},
{Old: "notify.telegram.timeout", New: "telegram.timeout", Version: "1.9"},
{Old: "notify.telegram.api", Version: "1.9"},
{Old: "auth.twitter.cid", Version: "1.14"},
{Old: "auth.twitter.csec", Version: "1.14"},
},
deprecatedFlags)
assert.Equal(t, "smtp.example.org", s.SMTP.Host)
@@ -697,7 +701,6 @@ func TestServerCommand_parseSameSite(t *testing.T) {
cmd := ServerCommand{}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, tt.res, cmd.parseSameSite(tt.inp))
})
-1
View File
@@ -88,7 +88,6 @@ func Test_initTemplatesErr(t *testing.T) {
}
for _, d := range testSet {
d := d
t.Run(d.name, func(t *testing.T) {
e, err := NewEmail(d.emailParams, ntf.SMTPParams{})
require.Error(t, err)
+2 -2
View File
@@ -624,9 +624,9 @@ func cacheControl(expiration time.Duration, version string) func(http.Handler) h
func securityHeadersMiddleware(imageProxyEnabled bool, allowedAncestors []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
imgSrc := "'self'"
imgSrc := "*"
if imageProxyEnabled {
imgSrc = "*"
imgSrc = "'self'"
}
frameAncestors := "*"
if len(allowedAncestors) > 0 {
@@ -916,7 +916,6 @@ func TestRest_EmailAndTelegram(t *testing.T) {
client := http.Client{}
defer client.CloseIdleConnections()
for _, x := range testData {
x := x
t.Run(x.description, func(t *testing.T) {
reqBody := io.NopCloser(strings.NewReader(x.body))
if x.body == "" {
+25 -5
View File
@@ -227,7 +227,6 @@ func Test_URLKey(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
r, err := http.NewRequest("GET", tt.url, http.NoBody)
require.NoError(t, err)
@@ -252,7 +251,6 @@ func Test_URLKeyWithUser(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
r, err := http.NewRequest("GET", tt.url, http.NoBody)
require.NoError(t, err)
@@ -279,7 +277,6 @@ func TestRest_parseError(t *testing.T) {
}
for n, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(n), func(t *testing.T) {
res := parseError(tt.err, rest.ErrInternal)
assert.Equal(t, tt.res, res)
@@ -302,7 +299,6 @@ func TestRest_cacheControl(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
req := httptest.NewRequest("GET", tt.url, http.NoBody)
w := httptest.NewRecorder()
@@ -345,6 +341,31 @@ func TestRest_frameAncestors(t *testing.T) {
assert.Contains(t, resp.Header.Get("Content-Security-Policy"), "frame-ancestors *;")
}
// check CSP, img-src should be 'self' with proxy enabled and * without it
func TestRest_securityHeaders(t *testing.T) {
ts, _, teardown := startupT(t)
// with proxy disabled
client := http.Client{}
resp, err := client.Get(ts.URL + "/web/index.html")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Security-Policy"), "img-src *;")
teardown()
// check CSP with proxy enabled
ts, _, teardown = startupT(t, func(srv *Rest) {
srv.ExternalImageProxy = true
})
defer teardown()
resp, err = client.Get(ts.URL + "/web/index.html")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Security-Policy"), "img-src 'self';")
}
func TestRest_subscribersOnly(t *testing.T) {
paidSubUser := &token.User{}
paidSubUser.SetPaidSub(true)
@@ -363,7 +384,6 @@ func TestRest_subscribersOnly(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", http.NoBody)
if tt.setUser {
-1
View File
@@ -77,7 +77,6 @@ func TestImage_Extract(t *testing.T) {
img := Image{HTTP2HTTPS: true}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
res, err := img.extract(tt.inp, func(src string) bool { return strings.HasPrefix(src, "http://") })
assert.NoError(t, err)
-3
View File
@@ -209,7 +209,6 @@ func TestComment_Snippet(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
c := Comment{Text: tt.inp}
out := c.Snippet(tt.limit)
@@ -245,7 +244,6 @@ func TestComment_sanitizeAsURL(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
c := Comment{}
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, tt.out, c.SanitizeAsURL(tt.inp))
@@ -276,7 +274,6 @@ func TestComment_sanitizeText(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
c := Comment{}
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, tt.out, c.SanitizeText(tt.inp))
-1
View File
@@ -865,7 +865,6 @@ func (b *BoltDB) deleteUser(bdb *bolt.DB, siteID, userID string, mode store.Dele
// get list of commentID for all user's comment
comments := []commentInfo{}
for _, postInfo := range posts {
postInfo := postInfo
err = bdb.View(func(tx *bolt.Tx) error {
postsBkt := tx.Bucket([]byte(postsBucketName))
postBkt := postsBkt.Bucket([]byte(postInfo.URL))
-2
View File
@@ -50,7 +50,6 @@ func TestFormatter_FormatText(t *testing.T) {
}
f := NewCommentFormatter(mockConverter{})
for _, tt := range tbl {
tt := tt
t.Run(tt.name, func(t *testing.T) {
raw := strings.HasPrefix(tt.in, `no_smartpants`)
t.Logf("raw: %v", raw)
@@ -146,7 +145,6 @@ func TestCommentFormatter_lazyImage(t *testing.T) {
f := NewCommentFormatter(nil)
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, tt.out, f.lazyImage(tt.inp))
})
-1
View File
@@ -173,7 +173,6 @@ func TestFsStore_location(t *testing.T) {
{0, "user/12345", "/tmp/user/12345"},
}
for n, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(n), func(t *testing.T) {
svc := FileSystem{Location: "/tmp", Partitions: tt.partitions}
assert.Equal(t, tt.res, svc.location("/tmp", tt.id))
-1
View File
@@ -259,7 +259,6 @@ func TestGetProportionalSizes(t *testing.T) {
}
for i, tt := range tbl {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
resW, resH := getProportionalSizes(tt.inpW, tt.inpH, tt.limitW, tt.limitH)
assert.Equal(t, tt.resW, resW, "width")
@@ -771,7 +771,6 @@ func TestService_Controversy(t *testing.T) {
b := DataStore{}
for i, tt := range tbl {
tt := tt
t.Run(fmt.Sprintf("check-%d-%d:%d", i, tt.ups, tt.downs), func(t *testing.T) {
assert.InDelta(t, tt.res, b.controversy(tt.ups, tt.downs), 0.01)
})
-1
View File
@@ -31,7 +31,6 @@ func TestTitle_GetTitle(t *testing.T) {
ex := NewTitleExtractor(http.Client{Timeout: 5 * time.Second}, []string{})
defer ex.Close()
for i, tt := range tbl {
tt := tt
t.Run(fmt.Sprintf("check-%d", i), func(t *testing.T) {
title, ok := ex.getTitle(strings.NewReader(tt.page))
assert.Equal(t, tt.ok, ok)
+16 -14
View File
@@ -1,11 +1,13 @@
module github.com/umputun/remark42/backend
go 1.21
go 1.23
toolchain go1.23.3
require (
github.com/Depado/bfchroma/v2 v2.0.0
github.com/PuerkitoBio/goquery v1.9.2
github.com/alecthomas/chroma/v2 v2.13.0
github.com/PuerkitoBio/goquery v1.10.0
github.com/alecthomas/chroma/v2 v2.14.0
github.com/didip/tollbooth/v7 v7.0.2
github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f
github.com/go-chi/chi/v5 v5.1.0
@@ -15,8 +17,8 @@ require (
github.com/go-pkgz/jrpc v0.3.0
github.com/go-pkgz/lcw/v2 v2.0.0
github.com/go-pkgz/lgr v0.11.1
github.com/go-pkgz/notify v1.1.1
github.com/go-pkgz/repeater v1.1.3
github.com/go-pkgz/notify v1.2.0
github.com/go-pkgz/repeater v1.2.0
github.com/go-pkgz/rest v1.19.0
github.com/go-pkgz/syncs v1.3.2
github.com/golang-jwt/jwt v3.2.2+incompatible
@@ -26,15 +28,15 @@ require (
github.com/jessevdk/go-flags v1.6.1
github.com/kyokomi/emoji/v2 v2.2.13
github.com/microcosm-cc/bluemonday v1.0.27
github.com/rs/xid v1.5.0
github.com/rs/xid v1.6.0
github.com/russross/blackfriday/v2 v2.1.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.9.0
go.etcd.io/bbolt v1.3.10
go.etcd.io/bbolt v1.3.11
go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.25.0
golang.org/x/image v0.18.0
golang.org/x/net v0.27.0
golang.org/x/crypto v0.27.0
golang.org/x/image v0.22.0
golang.org/x/net v0.29.0
)
require (
@@ -60,15 +62,15 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/redis/go-redis/v9 v9.4.0 // indirect
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d // indirect
github.com/slack-go/slack v0.12.5 // indirect
github.com/slack-go/slack v0.14.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver v1.16.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+28 -36
View File
@@ -3,14 +3,14 @@ cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJ
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8=
github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
@@ -60,14 +60,6 @@ github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-oauth2/oauth2/v4 v4.5.2 h1:CuZhD3lhGuI6aNLyUbRHXsgG2RwGRBOuCBfd4WQKqBQ=
github.com/go-oauth2/oauth2/v4 v4.5.2/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ=
github.com/go-pkgz/auth v1.24.0 h1:jo7Ke18nFGTWHdzmMKeF8zqXIC383Ptu/OXCkcQOZmI=
github.com/go-pkgz/auth v1.24.0/go.mod h1:xmnzq6g8mhemW1nHnkuByXkBXsHrNf9/qkiVwJugWIs=
github.com/go-pkgz/auth v1.24.1-0.20240919232608-9e446b888187 h1:1oHLySWdk0HfDIFzXFNqA6dEK7sTgZ/7s+l/YxYql7Q=
github.com/go-pkgz/auth v1.24.1-0.20240919232608-9e446b888187/go.mod h1:xmnzq6g8mhemW1nHnkuByXkBXsHrNf9/qkiVwJugWIs=
github.com/go-pkgz/auth v1.24.1 h1:izSFGxwNEZ2MujKJWXddKc+lUW+kVP02JBXouQIW8b4=
github.com/go-pkgz/auth v1.24.1/go.mod h1:xmnzq6g8mhemW1nHnkuByXkBXsHrNf9/qkiVwJugWIs=
github.com/go-pkgz/auth v1.24.2-0.20240921022538-30916c085e04 h1:DZzcdFb/EzXjQex8lDq/NqubVqwCXeyGxU5swYEVAYk=
github.com/go-pkgz/auth v1.24.2-0.20240921022538-30916c085e04/go.mod h1:xmnzq6g8mhemW1nHnkuByXkBXsHrNf9/qkiVwJugWIs=
github.com/go-pkgz/auth v1.24.2 h1:imMjUvTM0c8iOvP/GNGcuNcB/7gF3jFTF9dIPzlAOqI=
github.com/go-pkgz/auth v1.24.2/go.mod h1:xmnzq6g8mhemW1nHnkuByXkBXsHrNf9/qkiVwJugWIs=
github.com/go-pkgz/email v0.5.0 h1:fdtMDGJ8NwyBACLR0LYHaCIK/OeUwZHMhH7Q0+oty9U=
@@ -81,10 +73,10 @@ github.com/go-pkgz/lcw/v2 v2.0.0 h1:gTwXpiJBhQeA1rXuqkRuLcV79uATFna8CckH8ZBBrH0=
github.com/go-pkgz/lcw/v2 v2.0.0/go.mod h1:yxJHOn+IbQBQHxUqkCtMrbGjIfdYcsBAZcVCBaL1Va8=
github.com/go-pkgz/lgr v0.11.1 h1:hXFhZcznehI6imLhEa379oMOKFz7TQUmisAqb3oLOSM=
github.com/go-pkgz/lgr v0.11.1/go.mod h1:tgDF4RXQnBfIgJqjgkv0yOeTQ3F1yewWIZkpUhHnAkU=
github.com/go-pkgz/notify v1.1.1 h1:xjd5r0a9WUIKutrHkHfuNWdoXZsm84WNAoG35MD8QZA=
github.com/go-pkgz/notify v1.1.1/go.mod h1:8ri5Zx+IAjc87HB9kF9+KnVTm0yO5agsphvPbcRo54U=
github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE=
github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw=
github.com/go-pkgz/notify v1.2.0 h1:jqbsbWkodCDB9ffyI9fCG1bTSgidWJRS5UWD/twjU44=
github.com/go-pkgz/notify v1.2.0/go.mod h1:BTHj9ly7xZpaxg91lBdxCxSmkpVQ9R6q5QviX/upeYo=
github.com/go-pkgz/repeater v1.2.0 h1:oJFvjyKdTDd5RCzpzxlzYIZFFj6Zfl17rE1aUfu6UjQ=
github.com/go-pkgz/repeater v1.2.0/go.mod h1:vypP6xamA53MFmafnGUucqOmALKk36xgKu2hSG73LHM=
github.com/go-pkgz/rest v1.15.6/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk=
github.com/go-pkgz/rest v1.19.0 h1:FNMi5QX5dDIkuC+/e0r+CWsTuOTwUiWMRSA16Ou+9+A=
github.com/go-pkgz/rest v1.19.0/go.mod h1:Po+W6zQzpMPP6XDGLdAN2aW7UKk1IyrLSb48Lp1N3oQ=
@@ -176,8 +168,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d h1:l3+2LWCbVxn5itfvXAfH9n4YL9jh8l1g5zcncbIc1cs=
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d/go.mod h1:TbpErkob6SY7cyozRVSGoB3OlO2qOAgVN8O3KAJ4fMI=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
@@ -185,8 +177,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/slack-go/slack v0.12.5 h1:ddZ6uz6XVaB+3MTDhoW04gG+Vc/M/X1ctC+wssy2cqs=
github.com/slack-go/slack v0.12.5/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/slack-go/slack v0.14.0 h1:6c0UTfbRnvRssZUsZ2qe0Iu07VAMPjRqOa6oX8ewF4k=
github.com/slack-go/slack v0.14.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -255,8 +247,8 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -264,10 +256,10 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -283,8 +275,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
@@ -293,8 +285,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -313,8 +305,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -327,8 +319,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+15 -6
View File
@@ -22,7 +22,13 @@ Syntax-wise, it is as close as possible to jQuery, with the same function names
## Installation
Please note that starting with version `v1.9.0` of goquery, Go 1.18+ is required due to the use of generics. For previous goquery versions, a Go version of 1.1+ was required because of the `net/html` dependency. Ongoing goquery development is tested on the latest 2 versions of Go.
Required Go version:
* Starting with version `v1.10.0` of goquery, Go 1.23+ is required due to the use of function-based iterators.
* For `v1.9.0` of goquery, Go 1.18+ is required due to the use of generics.
* For previous goquery versions, a Go version of 1.1+ was required because of the `net/html` dependency.
Ongoing goquery development is tested on the latest 2 versions of Go.
$ go get github.com/PuerkitoBio/goquery
@@ -40,6 +46,8 @@ Please note that starting with version `v1.9.0` of goquery, Go 1.18+ is required
**Note that goquery's API is now stable, and will not break.**
* **2024-09-06 (v1.10.0)** : Add `EachIter` which provides an iterator that can be used in `for..range` loops on the `*Selection` object. **goquery now requires Go version 1.23+** (thanks [@amikai](https://github.com/amikai)).
* **2024-09-06 (v1.9.3)** : Update `go.mod` dependencies.
* **2024-04-29 (v1.9.2)** : Update `go.mod` dependencies.
* **2024-02-29 (v1.9.1)** : Improve allocation and performance of the `Map` function and `Selection.Map` method, better document the cascadia differences (thanks [@jwilsson](https://github.com/jwilsson)).
* **2024-02-22 (v1.9.0)** : Add a generic `Map` function, **goquery now requires Go version 1.18+** (thanks [@Fesaa](https://github.com/Fesaa)).
@@ -161,6 +169,7 @@ func main() {
- [goskyr](https://github.com/jakopako/goskyr), an easily configurable command-line scraper written in Go.
- [goGetJS](https://github.com/davemolk/goGetJS), a tool for extracting, searching, and saving JavaScript files (with optional headless browser).
- [fitter](https://github.com/PxyUp/fitter), a tool for selecting values from JSON, XML, HTML and XPath formatted pages.
- [seltabl](github.com/conneroisu/seltabl), an orm-like package and supporting language server for extracting values from HTML
## Support
@@ -183,15 +192,15 @@ There are a number of ways you can support the project:
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
[jquery]: http://jquery.com/
[go]: http://golang.org/
[jquery]: https://jquery.com/
[go]: https://go.dev/
[cascadia]: https://github.com/andybalholm/cascadia
[cascadiacli]: https://github.com/suntong/cascadia
[bsd]: http://opensource.org/licenses/BSD-3-Clause
[golic]: http://golang.org/LICENSE
[bsd]: https://opensource.org/licenses/BSD-3-Clause
[golic]: https://go.dev/LICENSE
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
[doc]: https://pkg.go.dev/github.com/PuerkitoBio/goquery
[index]: http://api.jquery.com/index/
[index]: https://api.jquery.com/index/
[gonet]: https://github.com/golang/net/
[html]: https://pkg.go.dev/golang.org/x/net/html
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
+1 -1
View File
@@ -62,7 +62,7 @@ func (s *Selection) AddBackFiltered(selector string) *Selection {
}
// AddBackMatcher reduces the previous set of elements on the stack to those that match
// the mateher, and adds them to the curernt set.
// the matcher, and adds them to the current set.
// It returns a new Selection object containing the current Selection combined
// with the filtered previous one
func (s *Selection) AddBackMatcher(m Matcher) *Selection {
+14
View File
@@ -1,5 +1,7 @@
package goquery
import "iter"
// Each iterates over a Selection object, executing a function for each
// matched element. It returns the current Selection object. The function
// f is called for each element in the selection with the index of the
@@ -12,6 +14,18 @@ func (s *Selection) Each(f func(int, *Selection)) *Selection {
return s
}
// EachIter returns an iterator that yields the Selection object in order.
// The implementation is similar to Each, but it returns an iterator instead.
func (s *Selection) EachIter() iter.Seq2[int, *Selection] {
return func(yield func(int, *Selection) bool) {
for i, n := range s.Nodes {
if !yield(i, newSingleSelection(n, s.document)) {
return
}
}
}
}
// EachWithBreak iterates over a Selection object, executing a function for each
// matched element. It is identical to Each except that it is possible to break
// out of the loop by returning false in the callback function. It returns the
+1 -1
View File
@@ -49,7 +49,7 @@
<rule pattern="(true|false|null|_)\b">
<token type="KeywordConstant"/>
</rule>
<rule pattern="#?[_a-zA-Z$]\w*">
<rule pattern="[@#]?[_a-zA-Z$]\w*">
<token type="Name"/>
</rule>
</state>
@@ -0,0 +1,117 @@
<lexer>
<config>
<name>Gleam</name>
<alias>gleam></alias>
<filename>*.gleam</filename>
<mime_type>text/x-gleam</mime_type>
</config>
<rules>
<state name="root">
<rule pattern="\s+">
<token type="TextWhitespace"/>
</rule>
<rule pattern="///(.*?)\n">
<token type="LiteralStringDoc"/>
</rule>
<rule pattern="//(.*?)\n">
<token type="CommentSingle"/>
</rule>
<rule pattern="(as|assert|case|opaque|panic|pub|todo)\b">
<token type="Keyword"/>
</rule>
<rule pattern="(import|use)\b">
<token type="KeywordNamespace"/>
</rule>
<rule pattern="(auto|const|delegate|derive|echo|else|if|implement|macro|test)\b">
<token type="KeywordReserved"/>
</rule>
<rule pattern="(let)\b">
<token type="KeywordDeclaration"/>
</rule>
<rule pattern="(fn)\b">
<token type="Keyword"/>
</rule>
<rule pattern="(type)\b">
<token type="Keyword"/>
<push state="typename"/>
</rule>
<rule pattern="(True|False)\b">
<token type="KeywordConstant"/>
</rule>
<rule pattern="0[bB][01](_?[01])*">
<token type="LiteralNumberBin"/>
</rule>
<rule pattern="0[oO][0-7](_?[0-7])*">
<token type="LiteralNumberOct"/>
</rule>
<rule pattern="0[xX][\da-fA-F](_?[\dA-Fa-f])*">
<token type="LiteralNumberHex"/>
</rule>
<rule pattern="\d(_?\d)*\.\d(_?\d)*([eE][-+]?\d(_?\d)*)?">
<token type="LiteralNumberFloat"/>
</rule>
<rule pattern="\d(_?\d)*">
<token type="LiteralNumberInteger"/>
</rule>
<rule pattern="&#34;">
<token type="LiteralString"/>
<push state="string"/>
</rule>
<rule pattern="@([a-z_]\w*[!?]?)">
<token type="NameAttribute"/>
</rule>
<rule pattern="[{}()\[\],]|[#(]|\.\.|&lt;&gt;|&lt;&lt;|&gt;&gt;">
<token type="Punctuation"/>
</rule>
<rule pattern="[+\-*/%!=&lt;&gt;&amp;|.]|&lt;-">
<token type="Operator"/>
</rule>
<rule pattern=":|-&gt;">
<token type="Operator"/>
<push state="typename"/>
</rule>
<rule pattern="([a-z_][A-Za-z0-9_]*)(\()">
<bygroups>
<token type="NameFunction"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="([A-Z][A-Za-z0-9_]*)(\()">
<bygroups>
<token type="NameClass"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="([a-z_]\w*[!?]?)">
<token type="Name"/>
</rule>
</state>
<state name="typename">
<rule pattern="\s+">
<token type="TextWhitespace"/>
</rule>
<rule pattern="[A-Z][A-Za-z0-9_]*">
<token type="NameClass"/>
<pop depth="1"/>
</rule>
<rule>
<pop depth="1"/>
</rule>
</state>
<state name="string">
<rule pattern="&#34;">
<token type="LiteralString"/>
<pop depth="1"/>
</rule>
<rule pattern="\\[&#34;\\fnrt]|\\u\{[\da-fA-F]{1,6}\}">
<token type="LiteralStringEscape"/>
</rule>
<rule pattern="[^\\&#34;]+">
<token type="LiteralString"/>
</rule>
<rule pattern="\\">
<token type="LiteralString"/>
</rule>
</state>
</rules>
</lexer>
@@ -228,42 +228,42 @@
</rule>
</state>
<state name="inline">
<rule pattern="(\s)*(\*[^ \n*][^*]+?[^ \n*]\*)((?=\W|\n|$))">
<rule pattern="(\s*)(\*[^ \n*][^*]+?[^ \n*]\*)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="GenericStrong"/>
<token type="Text"/>
</bygroups>
</rule>
<rule pattern="(\s)*(/[^/]+?/)((?=\W|\n|$))">
<rule pattern="(\s*)(/[^/]+?/)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="GenericEmph"/>
<token type="Text"/>
</bygroups>
</rule>
<rule pattern="(\s)*(=[^\n=]+?=)((?=\W|\n|$))">
<rule pattern="(\s*)(=[^\n=]+?=)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="NameClass"/>
<token type="Text"/>
</bygroups>
</rule>
<rule pattern="(\s)*(~[^\n~]+?~)((?=\W|\n|$))">
<rule pattern="(\s*)(~[^\n~]+?~)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="NameClass"/>
<token type="Text"/>
</bygroups>
</rule>
<rule pattern="(\s)*(\+[^+]+?\+)((?=\W|\n|$))">
<rule pattern="(\s*)(\+[^+]+?\+)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="GenericDeleted"/>
<token type="Text"/>
</bygroups>
</rule>
<rule pattern="(\s)*(_[^_]+?_)((?=\W|\n|$))">
<rule pattern="(\s*)(_[^_]+?_)((?=\W|\n|$))">
<bygroups>
<token type="Text"/>
<token type="GenericUnderline"/>
+5 -1
View File
@@ -19,6 +19,10 @@
<filename>BUILD</filename>
<filename>BUILD.bazel</filename>
<filename>WORKSPACE</filename>
<filename>WORKSPACE.bzlmod</filename>
<filename>WORKSPACE.bazel</filename>
<filename>MODULE.bazel</filename>
<filename>REPO.bazel</filename>
<filename>*.tac</filename>
<mime_type>text/x-python</mime_type>
<mime_type>application/x-python</mime_type>
@@ -586,4 +590,4 @@
</rule>
</state>
</rules>
</lexer>
</lexer>
@@ -51,10 +51,13 @@
</rule>
</state>
<state name="tag">
<rule>
<include state="commentsandwhitespace"/>
</rule>
<rule pattern="\s+">
<token type="Text"/>
</rule>
<rule pattern="([\w]+\s*)(=)(\s*)">
<rule pattern="([\w-]+\s*)(=)(\s*)">
<bygroups>
<token type="NameAttribute"/>
<token type="Operator"/>
@@ -77,12 +80,25 @@
<pop depth="1"/>
</rule>
</state>
<state name="comment">
<rule pattern="[^-]+">
<token type="Comment"/>
</rule>
<rule pattern="--&gt;">
<token type="Comment"/>
<pop depth="1"/>
</rule>
<rule pattern="-">
<token type="Comment"/>
</rule>
</state>
<state name="commentsandwhitespace">
<rule pattern="\s+">
<token type="Text"/>
</rule>
<rule pattern="&lt;!--">
<token type="Comment"/>
<push state="comment"/>
</rule>
<rule pattern="//.*?\n">
<token type="CommentSingle"/>
@@ -200,7 +216,7 @@
<rule pattern="(Array|Boolean|Date|Error|Function|Math|Number|Object|Packages|RegExp|String|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|document|this|window)\b">
<token type="NameBuiltin"/>
</rule>
<rule pattern="\b(module)(\s*)(\s*[\w?.$][\w?.$]*)(\s*)">
<rule pattern="\b(module)(\s+)(&quot;[\w\./@]+&quot;)(\s+)">
<bygroups>
<token type="KeywordReserved"/>
<token type="Text"/>
+6 -4
View File
@@ -83,9 +83,10 @@
<token type="LiteralString"/>
</bygroups>
</rule>
<rule pattern="(:[\S]+)(=&#34;[\S]+&#34;)">
<rule pattern="(:[\S]+)(=)(&#34;[\S]+&#34;)">
<bygroups>
<token type="NameTag"/>
<token type="Operator"/>
<token type="LiteralString"/>
</bygroups>
</rule>
@@ -104,9 +105,10 @@
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="(v-[\w]+)(=&#34;[\S]+&#34;)(&gt;)">
<rule pattern="(v-[\w]+)(=)(&#34;[\S ]+&#34;)(&gt;|\s)">
<bygroups>
<token type="NameTag"/>
<token type="Operator"/>
<token type="LiteralString"/>
<token type="Punctuation"/>
</bygroups>
@@ -258,14 +260,14 @@
</rule>
</state>
<state name="vue">
<rule pattern="(&lt;)([\w]+)">
<rule pattern="(&lt;)([\w-]+)">
<bygroups>
<token type="Punctuation"/>
<token type="NameTag"/>
</bygroups>
<push state="tag"/>
</rule>
<rule pattern="(&lt;)(/)([\w]+)(&gt;)">
<rule pattern="(&lt;)(/)([\w-]+)(&gt;)">
<bygroups>
<token type="Punctuation"/>
<token type="Punctuation"/>
@@ -0,0 +1,83 @@
<style name="tokyonight-day">
<entry type="Background" style="bg:#e1e2e7 #3760bf"/>
<entry type="CodeLine" style="#3760bf"/>
<entry type="Error" style="#c64343"/>
<entry type="Other" style="#3760bf"/>
<entry type="LineTableTD" style=""/>
<entry type="LineTable" style=""/>
<entry type="LineHighlight" style="bg:#a1a6c5"/>
<entry type="LineNumbersTable" style="#6172b0"/>
<entry type="LineNumbers" style="#6172b0"/>
<entry type="Keyword" style="#9854f1"/>
<entry type="KeywordReserved" style="#9854f1"/>
<entry type="KeywordPseudo" style="#9854f1"/>
<entry type="KeywordConstant" style="#8c6c3e"/>
<entry type="KeywordDeclaration" style="#9d7cd8"/>
<entry type="KeywordNamespace" style="#007197"/>
<entry type="KeywordType" style="#0db9d7"/>
<entry type="Name" style="#3760bf"/>
<entry type="NameClass" style="#b15c00"/>
<entry type="NameConstant" style="#b15c00"/>
<entry type="NameDecorator" style="bold #2e7de9"/>
<entry type="NameEntity" style="#007197"/>
<entry type="NameException" style="#8c6c3e"/>
<entry type="NameFunction" style="#2e7de9"/>
<entry type="NameFunctionMagic" style="#2e7de9"/>
<entry type="NameLabel" style="#587539"/>
<entry type="NameNamespace" style="#8c6c3e"/>
<entry type="NameProperty" style="#8c6c3e"/>
<entry type="NameTag" style="#9854f1"/>
<entry type="NameVariable" style="#3760bf"/>
<entry type="NameVariableClass" style="#3760bf"/>
<entry type="NameVariableGlobal" style="#3760bf"/>
<entry type="NameVariableInstance" style="#3760bf"/>
<entry type="NameVariableMagic" style="#3760bf"/>
<entry type="NameAttribute" style="#2e7de9"/>
<entry type="NameBuiltin" style="#587539"/>
<entry type="NameBuiltinPseudo" style="#587539"/>
<entry type="NameOther" style="#3760bf"/>
<entry type="Literal" style="#3760bf"/>
<entry type="LiteralDate" style="#3760bf"/>
<entry type="LiteralString" style="#587539"/>
<entry type="LiteralStringChar" style="#587539"/>
<entry type="LiteralStringSingle" style="#587539"/>
<entry type="LiteralStringDouble" style="#587539"/>
<entry type="LiteralStringBacktick" style="#587539"/>
<entry type="LiteralStringOther" style="#587539"/>
<entry type="LiteralStringSymbol" style="#587539"/>
<entry type="LiteralStringInterpol" style="#587539"/>
<entry type="LiteralStringAffix" style="#9d7cd8"/>
<entry type="LiteralStringDelimiter" style="#2e7de9"/>
<entry type="LiteralStringEscape" style="#2e7de9"/>
<entry type="LiteralStringRegex" style="#007197"/>
<entry type="LiteralStringDoc" style="#a1a6c5"/>
<entry type="LiteralStringHeredoc" style="#a1a6c5"/>
<entry type="LiteralNumber" style="#8c6c3e"/>
<entry type="LiteralNumberBin" style="#8c6c3e"/>
<entry type="LiteralNumberHex" style="#8c6c3e"/>
<entry type="LiteralNumberInteger" style="#8c6c3e"/>
<entry type="LiteralNumberFloat" style="#8c6c3e"/>
<entry type="LiteralNumberIntegerLong" style="#8c6c3e"/>
<entry type="LiteralNumberOct" style="#8c6c3e"/>
<entry type="Operator" style="bold #587539"/>
<entry type="OperatorWord" style="bold #587539"/>
<entry type="Comment" style="italic #a1a6c5"/>
<entry type="CommentSingle" style="italic #a1a6c5"/>
<entry type="CommentMultiline" style="italic #a1a6c5"/>
<entry type="CommentSpecial" style="italic #a1a6c5"/>
<entry type="CommentHashbang" style="italic #a1a6c5"/>
<entry type="CommentPreproc" style="italic #a1a6c5"/>
<entry type="CommentPreprocFile" style="bold #a1a6c5"/>
<entry type="Generic" style="#3760bf"/>
<entry type="GenericInserted" style="bg:#e9e9ed #587539"/>
<entry type="GenericDeleted" style="#c64343 bg:#e9e9ed"/>
<entry type="GenericEmph" style="italic #3760bf"/>
<entry type="GenericStrong" style="bold #3760bf"/>
<entry type="GenericUnderline" style="underline #3760bf"/>
<entry type="GenericHeading" style="bold #8c6c3e"/>
<entry type="GenericSubheading" style="bold #8c6c3e"/>
<entry type="GenericOutput" style="#3760bf"/>
<entry type="GenericPrompt" style="#3760bf"/>
<entry type="GenericError" style="#c64343"/>
<entry type="GenericTraceback" style="#c64343"/>
</style>
@@ -0,0 +1,83 @@
<style name="tokyonight-moon">
<entry type="Background" style="bg:#222436 #c8d3f5"/>
<entry type="CodeLine" style="#c8d3f5"/>
<entry type="Error" style="#c53b53"/>
<entry type="Other" style="#c8d3f5"/>
<entry type="LineTableTD" style=""/>
<entry type="LineTable" style=""/>
<entry type="LineHighlight" style="bg:#444a73"/>
<entry type="LineNumbersTable" style="#828bb8"/>
<entry type="LineNumbers" style="#828bb8"/>
<entry type="Keyword" style="#c099ff"/>
<entry type="KeywordReserved" style="#c099ff"/>
<entry type="KeywordPseudo" style="#c099ff"/>
<entry type="KeywordConstant" style="#ffc777"/>
<entry type="KeywordDeclaration" style="#c099ff"/>
<entry type="KeywordNamespace" style="#86e1fc"/>
<entry type="KeywordType" style="#4fd6be"/>
<entry type="Name" style="#c8d3f5"/>
<entry type="NameClass" style="#ff966c"/>
<entry type="NameConstant" style="#ff966c"/>
<entry type="NameDecorator" style="bold #82aaff"/>
<entry type="NameEntity" style="#86e1fc"/>
<entry type="NameException" style="#ffc777"/>
<entry type="NameFunction" style="#82aaff"/>
<entry type="NameFunctionMagic" style="#82aaff"/>
<entry type="NameLabel" style="#c3e88d"/>
<entry type="NameNamespace" style="#ffc777"/>
<entry type="NameProperty" style="#ffc777"/>
<entry type="NameTag" style="#c099ff"/>
<entry type="NameVariable" style="#c8d3f5"/>
<entry type="NameVariableClass" style="#c8d3f5"/>
<entry type="NameVariableGlobal" style="#c8d3f5"/>
<entry type="NameVariableInstance" style="#c8d3f5"/>
<entry type="NameVariableMagic" style="#c8d3f5"/>
<entry type="NameAttribute" style="#82aaff"/>
<entry type="NameBuiltin" style="#c3e88d"/>
<entry type="NameBuiltinPseudo" style="#c3e88d"/>
<entry type="NameOther" style="#c8d3f5"/>
<entry type="Literal" style="#c8d3f5"/>
<entry type="LiteralDate" style="#c8d3f5"/>
<entry type="LiteralString" style="#c3e88d"/>
<entry type="LiteralStringChar" style="#c3e88d"/>
<entry type="LiteralStringSingle" style="#c3e88d"/>
<entry type="LiteralStringDouble" style="#c3e88d"/>
<entry type="LiteralStringBacktick" style="#c3e88d"/>
<entry type="LiteralStringOther" style="#c3e88d"/>
<entry type="LiteralStringSymbol" style="#c3e88d"/>
<entry type="LiteralStringInterpol" style="#c3e88d"/>
<entry type="LiteralStringAffix" style="#c099ff"/>
<entry type="LiteralStringDelimiter" style="#82aaff"/>
<entry type="LiteralStringEscape" style="#82aaff"/>
<entry type="LiteralStringRegex" style="#86e1fc"/>
<entry type="LiteralStringDoc" style="#444a73"/>
<entry type="LiteralStringHeredoc" style="#444a73"/>
<entry type="LiteralNumber" style="#ffc777"/>
<entry type="LiteralNumberBin" style="#ffc777"/>
<entry type="LiteralNumberHex" style="#ffc777"/>
<entry type="LiteralNumberInteger" style="#ffc777"/>
<entry type="LiteralNumberFloat" style="#ffc777"/>
<entry type="LiteralNumberIntegerLong" style="#ffc777"/>
<entry type="LiteralNumberOct" style="#ffc777"/>
<entry type="Operator" style="bold #c3e88d"/>
<entry type="OperatorWord" style="bold #c3e88d"/>
<entry type="Comment" style="italic #444a73"/>
<entry type="CommentSingle" style="italic #444a73"/>
<entry type="CommentMultiline" style="italic #444a73"/>
<entry type="CommentSpecial" style="italic #444a73"/>
<entry type="CommentHashbang" style="italic #444a73"/>
<entry type="CommentPreproc" style="italic #444a73"/>
<entry type="CommentPreprocFile" style="bold #444a73"/>
<entry type="Generic" style="#c8d3f5"/>
<entry type="GenericInserted" style="bg:#1b1d2b #c3e88d"/>
<entry type="GenericDeleted" style="#c53b53 bg:#1b1d2b"/>
<entry type="GenericEmph" style="italic #c8d3f5"/>
<entry type="GenericStrong" style="bold #c8d3f5"/>
<entry type="GenericUnderline" style="underline #c8d3f5"/>
<entry type="GenericHeading" style="bold #ffc777"/>
<entry type="GenericSubheading" style="bold #ffc777"/>
<entry type="GenericOutput" style="#c8d3f5"/>
<entry type="GenericPrompt" style="#c8d3f5"/>
<entry type="GenericError" style="#c53b53"/>
<entry type="GenericTraceback" style="#c53b53"/>
</style>
@@ -0,0 +1,83 @@
<style name="tokyonight-night">
<entry type="Background" style="bg:#1a1b26 #c0caf5"/>
<entry type="CodeLine" style="#c0caf5"/>
<entry type="Error" style="#db4b4b"/>
<entry type="Other" style="#c0caf5"/>
<entry type="LineTableTD" style=""/>
<entry type="LineTable" style=""/>
<entry type="LineHighlight" style="bg:#414868"/>
<entry type="LineNumbersTable" style="#a9b1d6"/>
<entry type="LineNumbers" style="#a9b1d6"/>
<entry type="Keyword" style="#bb9af7"/>
<entry type="KeywordReserved" style="#bb9af7"/>
<entry type="KeywordPseudo" style="#bb9af7"/>
<entry type="KeywordConstant" style="#e0af68"/>
<entry type="KeywordDeclaration" style="#9d7cd8"/>
<entry type="KeywordNamespace" style="#7dcfff"/>
<entry type="KeywordType" style="#41a6b5"/>
<entry type="Name" style="#c0caf5"/>
<entry type="NameClass" style="#ff9e64"/>
<entry type="NameConstant" style="#ff9e64"/>
<entry type="NameDecorator" style="bold #7aa2f7"/>
<entry type="NameEntity" style="#7dcfff"/>
<entry type="NameException" style="#e0af68"/>
<entry type="NameFunction" style="#7aa2f7"/>
<entry type="NameFunctionMagic" style="#7aa2f7"/>
<entry type="NameLabel" style="#9ece6a"/>
<entry type="NameNamespace" style="#e0af68"/>
<entry type="NameProperty" style="#e0af68"/>
<entry type="NameTag" style="#bb9af7"/>
<entry type="NameVariable" style="#c0caf5"/>
<entry type="NameVariableClass" style="#c0caf5"/>
<entry type="NameVariableGlobal" style="#c0caf5"/>
<entry type="NameVariableInstance" style="#c0caf5"/>
<entry type="NameVariableMagic" style="#c0caf5"/>
<entry type="NameAttribute" style="#7aa2f7"/>
<entry type="NameBuiltin" style="#9ece6a"/>
<entry type="NameBuiltinPseudo" style="#9ece6a"/>
<entry type="NameOther" style="#c0caf5"/>
<entry type="Literal" style="#c0caf5"/>
<entry type="LiteralDate" style="#c0caf5"/>
<entry type="LiteralString" style="#9ece6a"/>
<entry type="LiteralStringChar" style="#9ece6a"/>
<entry type="LiteralStringSingle" style="#9ece6a"/>
<entry type="LiteralStringDouble" style="#9ece6a"/>
<entry type="LiteralStringBacktick" style="#9ece6a"/>
<entry type="LiteralStringOther" style="#9ece6a"/>
<entry type="LiteralStringSymbol" style="#9ece6a"/>
<entry type="LiteralStringInterpol" style="#9ece6a"/>
<entry type="LiteralStringAffix" style="#9d7cd8"/>
<entry type="LiteralStringDelimiter" style="#7aa2f7"/>
<entry type="LiteralStringEscape" style="#7aa2f7"/>
<entry type="LiteralStringRegex" style="#7dcfff"/>
<entry type="LiteralStringDoc" style="#414868"/>
<entry type="LiteralStringHeredoc" style="#414868"/>
<entry type="LiteralNumber" style="#e0af68"/>
<entry type="LiteralNumberBin" style="#e0af68"/>
<entry type="LiteralNumberHex" style="#e0af68"/>
<entry type="LiteralNumberInteger" style="#e0af68"/>
<entry type="LiteralNumberFloat" style="#e0af68"/>
<entry type="LiteralNumberIntegerLong" style="#e0af68"/>
<entry type="LiteralNumberOct" style="#e0af68"/>
<entry type="Operator" style="bold #9ece6a"/>
<entry type="OperatorWord" style="bold #9ece6a"/>
<entry type="Comment" style="italic #414868"/>
<entry type="CommentSingle" style="italic #414868"/>
<entry type="CommentMultiline" style="italic #414868"/>
<entry type="CommentSpecial" style="italic #414868"/>
<entry type="CommentHashbang" style="italic #414868"/>
<entry type="CommentPreproc" style="italic #414868"/>
<entry type="CommentPreprocFile" style="bold #414868"/>
<entry type="Generic" style="#c0caf5"/>
<entry type="GenericInserted" style="bg:#15161e #9ece6a"/>
<entry type="GenericDeleted" style="#db4b4b bg:#15161e"/>
<entry type="GenericEmph" style="italic #c0caf5"/>
<entry type="GenericStrong" style="bold #c0caf5"/>
<entry type="GenericUnderline" style="underline #c0caf5"/>
<entry type="GenericHeading" style="bold #e0af68"/>
<entry type="GenericSubheading" style="bold #e0af68"/>
<entry type="GenericOutput" style="#c0caf5"/>
<entry type="GenericPrompt" style="#c0caf5"/>
<entry type="GenericError" style="#db4b4b"/>
<entry type="GenericTraceback" style="#db4b4b"/>
</style>
@@ -0,0 +1,83 @@
<style name="tokyonight-storm">
<entry type="Background" style="bg:#1a1b26 #c0caf5"/>
<entry type="CodeLine" style="#c0caf5"/>
<entry type="Error" style="#db4b4b"/>
<entry type="Other" style="#c0caf5"/>
<entry type="LineTableTD" style=""/>
<entry type="LineTable" style=""/>
<entry type="LineHighlight" style="bg:#414868"/>
<entry type="LineNumbersTable" style="#a9b1d6"/>
<entry type="LineNumbers" style="#a9b1d6"/>
<entry type="Keyword" style="#bb9af7"/>
<entry type="KeywordReserved" style="#bb9af7"/>
<entry type="KeywordPseudo" style="#bb9af7"/>
<entry type="KeywordConstant" style="#e0af68"/>
<entry type="KeywordDeclaration" style="#9d7cd8"/>
<entry type="KeywordNamespace" style="#7dcfff"/>
<entry type="KeywordType" style="#41a6b5"/>
<entry type="Name" style="#c0caf5"/>
<entry type="NameClass" style="#ff9e64"/>
<entry type="NameConstant" style="#ff9e64"/>
<entry type="NameDecorator" style="bold #7aa2f7"/>
<entry type="NameEntity" style="#7dcfff"/>
<entry type="NameException" style="#e0af68"/>
<entry type="NameFunction" style="#7aa2f7"/>
<entry type="NameFunctionMagic" style="#7aa2f7"/>
<entry type="NameLabel" style="#9ece6a"/>
<entry type="NameNamespace" style="#e0af68"/>
<entry type="NameProperty" style="#e0af68"/>
<entry type="NameTag" style="#bb9af7"/>
<entry type="NameVariable" style="#c0caf5"/>
<entry type="NameVariableClass" style="#c0caf5"/>
<entry type="NameVariableGlobal" style="#c0caf5"/>
<entry type="NameVariableInstance" style="#c0caf5"/>
<entry type="NameVariableMagic" style="#c0caf5"/>
<entry type="NameAttribute" style="#7aa2f7"/>
<entry type="NameBuiltin" style="#9ece6a"/>
<entry type="NameBuiltinPseudo" style="#9ece6a"/>
<entry type="NameOther" style="#c0caf5"/>
<entry type="Literal" style="#c0caf5"/>
<entry type="LiteralDate" style="#c0caf5"/>
<entry type="LiteralString" style="#9ece6a"/>
<entry type="LiteralStringChar" style="#9ece6a"/>
<entry type="LiteralStringSingle" style="#9ece6a"/>
<entry type="LiteralStringDouble" style="#9ece6a"/>
<entry type="LiteralStringBacktick" style="#9ece6a"/>
<entry type="LiteralStringOther" style="#9ece6a"/>
<entry type="LiteralStringSymbol" style="#9ece6a"/>
<entry type="LiteralStringInterpol" style="#9ece6a"/>
<entry type="LiteralStringAffix" style="#9d7cd8"/>
<entry type="LiteralStringDelimiter" style="#7aa2f7"/>
<entry type="LiteralStringEscape" style="#7aa2f7"/>
<entry type="LiteralStringRegex" style="#7dcfff"/>
<entry type="LiteralStringDoc" style="#414868"/>
<entry type="LiteralStringHeredoc" style="#414868"/>
<entry type="LiteralNumber" style="#e0af68"/>
<entry type="LiteralNumberBin" style="#e0af68"/>
<entry type="LiteralNumberHex" style="#e0af68"/>
<entry type="LiteralNumberInteger" style="#e0af68"/>
<entry type="LiteralNumberFloat" style="#e0af68"/>
<entry type="LiteralNumberIntegerLong" style="#e0af68"/>
<entry type="LiteralNumberOct" style="#e0af68"/>
<entry type="Operator" style="bold #9ece6a"/>
<entry type="OperatorWord" style="bold #9ece6a"/>
<entry type="Comment" style="italic #414868"/>
<entry type="CommentSingle" style="italic #414868"/>
<entry type="CommentMultiline" style="italic #414868"/>
<entry type="CommentSpecial" style="italic #414868"/>
<entry type="CommentHashbang" style="italic #414868"/>
<entry type="CommentPreproc" style="italic #414868"/>
<entry type="CommentPreprocFile" style="bold #414868"/>
<entry type="Generic" style="#c0caf5"/>
<entry type="GenericInserted" style="bg:#15161e #9ece6a"/>
<entry type="GenericDeleted" style="#db4b4b bg:#15161e"/>
<entry type="GenericEmph" style="italic #c0caf5"/>
<entry type="GenericStrong" style="bold #c0caf5"/>
<entry type="GenericUnderline" style="underline #c0caf5"/>
<entry type="GenericHeading" style="bold #e0af68"/>
<entry type="GenericSubheading" style="bold #e0af68"/>
<entry type="GenericOutput" style="#c0caf5"/>
<entry type="GenericPrompt" style="#c0caf5"/>
<entry type="GenericError" style="#db4b4b"/>
<entry type="GenericTraceback" style="#db4b4b"/>
</style>
+5 -2
View File
@@ -131,13 +131,16 @@ func (t *Telegram) Send(ctx context.Context, destination, text string) error {
// TelegramSupportedHTML returns HTML with only tags allowed in Telegram HTML message payload, also trims ending newlines
//
// https://core.telegram.org/bots/api#html-style
// https://core.telegram.org/bots/api#html-style, https://core.telegram.org/api/entities#allowed-entities
func TelegramSupportedHTML(htmlText string) string {
adjustedHTMLText := adjustHTMLTags(htmlText)
p := bluemonday.NewPolicy()
p.AllowElements("b", "strong", "i", "em", "u", "ins", "s", "strike", "del", "a", "code", "pre")
p.AllowElements("b", "strong", "i", "em", "u", "ins", "s", "strike", "del", "a", "code", "pre", "tg-spoiler", "tg-emoji", "blockquote")
p.AllowAttrs("href").OnElements("a")
p.AllowAttrs("class").OnElements("code")
p.AllowAttrs("title").OnElements("tg-spoiler")
p.AllowAttrs("emoji-id").OnElements("tg-emoji")
p.AllowAttrs("language").OnElements("pre")
return strings.TrimRight(p.Sanitize(adjustedHTMLText), "\n")
}
+1 -1
View File
@@ -69,7 +69,7 @@ func (wh *Webhook) Send(ctx context.Context, destination, text string) error {
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
errMsg := fmt.Sprintf("webhook request failed with non-OK status code: %d", resp.StatusCode)
respBody, e := io.ReadAll(resp.Body)
if e != nil {
+36 -13
View File
@@ -2,7 +2,7 @@ linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
min-confidence: 0.6
gocyclo:
min-complexity: 15
maligned:
@@ -23,38 +23,61 @@ linters-settings:
- experimental
disabled-checks:
- wrapperFunc
- hugeParam
- rangeValCopy
linters:
disable-all: true
enable:
- megacheck
- revive
- govet
- unconvert
- megacheck
- structcheck
- gas
- gocyclo
- dupl
- misspell
- unparam
- varcheck
- deadcode
- unused
- typecheck
- ineffassign
- varcheck
- stylecheck
- gochecknoinits
- exportloopref
- nakedret
- gosimple
- prealloc
fast: false
run:
# modules-download-mode: vendor
# modules-download-mode: vendor
skip-dirs:
- vendor
concurrency: 4
issues:
exclude-rules:
- text: "should have a package comment, unless it's in another file for this package"
linters:
- golint
- text: "exitAfterDefer:"
linters:
- gocritic
- text: "whyNoLint: include an explanation for nolint directive"
linters:
- gocritic
- text: "go.mongodb.org/mongo-driver/bson/primitive.E"
linters:
- govet
- text: "weak cryptographic primitive"
linters:
- gosec
service:
golangci-lint-version: 1.16.x
- text: "at least one file in a package should have a package comment"
linters:
- stylecheck
- text: "should have a package comment"
linters:
- revive
- text: 'Deferring unsafe method "Close" on type "io.ReadCloser"'
linters:
- gosec
exclude-use-default: false
-20
View File
@@ -1,20 +0,0 @@
language: go
go:
- "1.12.x"
install: true
before_install:
- export TZ=America/Chicago
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.13.2
- go get github.com/mattn/goveralls
- export PATH=$(pwd)/bin:$PATH
script:
- GO111MODULE=on go get ./...
- GO111MODULE=on go mod vendor
- GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
- GO111MODULE=on go test -v -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
- golangci-lint run || travis_terminate 1;
- $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Umputun
Copyright (c) 2023 Umputun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+8 -10
View File
@@ -1,4 +1,4 @@
# Repeater [![Build Status](https://travis-ci.org/go-pkgz/repeater.svg?branch=master)](https://travis-ci.org/go-pkgz/repeater) [![Go Report Card](https://goreportcard.com/badge/github.com/go-pkgz/repeater)](https://goreportcard.com/report/github.com/go-pkgz/repeater) [![Coverage Status](https://coveralls.io/repos/github/go-pkgz/repeater/badge.svg?branch=master)](https://coveralls.io/github/go-pkgz/repeater?branch=master)
# Repeater [![Build Status](https://github.com/go-pkgz/repeater/workflows/build/badge.svg)](https://github.com/go-pkgz/repeater/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/go-pkgz/repeater)](https://goreportcard.com/report/github.com/go-pkgz/repeater) [![Coverage Status](https://coveralls.io/repos/github/go-pkgz/repeater/badge.svg?branch=master)](https://coveralls.io/github/go-pkgz/repeater?branch=master)
Repeater calls a function until it returns no error, up to some number of iterations and delays defined by strategy. It terminates immediately on err from the provided (optional) list of critical errors.
@@ -8,9 +8,9 @@ Repeater calls a function until it returns no error, up to some number of iterat
## How to use
New Repeater created by `New(strtg strategy.Interface)` or shortcut for defaults - `NewDefault(repeats int, delay time.Duration) *Repeater`.
New Repeater created by `New(strtg strategy.Interface)` or shortcut for default - `NewDefault(repeats int, delay time.Duration) *Repeater`.
To activate invoke `Do` method. `Do` repeats func until no error returned. Predefined (optional) errors terminates the loop immediately.
To activate invoke `Do` method. `Do` repeats func until no error returned. Predefined (optional) errors terminate the loop immediately.
`func (r Repeater) Do(ctx context.Context, fun func() error, errors ...error) (err error)`
@@ -24,12 +24,10 @@ type Interface interface {
}
```
Returned channels used as "ticks," i.e., for each repeat or initial operation one read from this channel needed. Closing this channel indicates "done with retries." It is pretty much the same idea as `time.Timer` or `time.Tick` implements. Note - the first (technically not-repeated-yet) call won't happen **until something sent to the channel**. For this reason, the typical strategy sends the first "tick" before the first wait/sleep.
Three most common strategies provided by package and ready to use:
1. **Fixed delay**, up to max number of attempts - `NewFixedDelay(repeats int, delay time.Duration)`.
It is the default strategy used by `repeater.NewDefault` constructor
2. **BackOff** with jitter provides exponential backoff. It starts from 100ms interval and goes in steps with `last * math.Pow(factor, attempt)`. Optional jitter randomizes intervals a little bit. The strategy created by `NewBackoff(repeats int, factor float64, jitter bool)`. _Factor = 1 effectively makes this strategy fixed with 100ms delay._
3. **Once** strategy does not do any repeats and mainly used for tests/mocks - `NewOnce()`
Returned channels used as "ticks," i.e., for each repeat or initial operation one read from this channel needed. Closing the channel indicates "done with retries." It is pretty much the same idea as `time.Timer` or `time.Tick` implements. Note - the first (technically not-repeated-yet) call won't happen **until something sent to the channel**. For this reason, the typical strategy sends the first "tick" before the first wait/sleep.
Three strategies provided byt the package:
1. **Fixed delay**, up to max number of attempts. It is the default strategy used by `repeater.NewDefault` constructor.
2. **BackOff** with jitter provides an exponential backoff. It starts from `Duration` interval and goes in steps with `last * math.Pow(factor, attempt)`. Optional jitter randomizes intervals a little. _Factor = 1 effectively makes this strategy fixed with `Duration` delay._
3. **Once** strategy does not do any repeats and mainly used for tests/mocks`.
+11 -6
View File
@@ -5,6 +5,7 @@ package repeater
import (
"context"
"errors"
"time"
"github.com/go-pkgz/repeater/strategy"
@@ -12,7 +13,12 @@ import (
// Repeater is the main object, should be made by New or NewDefault, embeds strategy
type Repeater struct {
strategy.Interface
Strategy
}
// Strategy interface for repeater strategy
type Strategy interface {
Start(ctx context.Context) <-chan struct{} // returns channel with repeater ticks
}
// New repeater with a given strategy. If strategy=nil initializes with FixedDelay 5sec, 10 times.
@@ -20,7 +26,7 @@ func New(strtg strategy.Interface) *Repeater {
if strtg == nil {
strtg = &strategy.FixedDelay{Repeats: 10, Delay: time.Second * 5}
}
result := Repeater{Interface: strtg}
result := Repeater{Strategy: strtg}
return &result
}
@@ -30,14 +36,13 @@ func NewDefault(repeats int, delay time.Duration) *Repeater {
}
// Do repeats fun till no error. Predefined (optional) errors terminate immediately
func (r Repeater) Do(ctx context.Context, fun func() error, errors ...error) (err error) {
func (r Repeater) Do(ctx context.Context, fun func() error, errs ...error) (err error) {
ctx, cancelFunc := context.WithCancel(ctx)
defer cancelFunc() // ensure strategy's channel termination
inErrors := func(err error) bool {
for _, e := range errors {
if e == err {
for _, e := range errs {
if errors.Is(err, e) {
return true
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ func (b *Backoff) Start(ctx context.Context) <-chan struct{} {
ch := make(chan struct{})
go func() {
defer close(ch)
rnd := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
rnd := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) //nolint:gosec
for i := 0; i < b.Repeats; i++ {
select {
case <-ctx.Done():
+1 -1
View File
@@ -16,7 +16,7 @@ type Interface interface {
type Once struct{}
// Start returns closed channel with a single element to prevent any repeats
func (s *Once) Start(ctx context.Context) <-chan struct{} {
func (s *Once) Start(_ context.Context) <-chan struct{} {
ch := make(chan struct{})
go func() {
ch <- struct{}{}
+3
View File
@@ -0,0 +1,3 @@
/.idea
/.vscode
.DS_Store
+6 -4
View File
@@ -4,7 +4,7 @@
Package xid is a globally unique id generator library, ready to safely be used directly in your server code.
Xid uses the Mongo Object ID algorithm to generate globally unique ids with a different serialization (base64) to make it shorter when transported as a string:
Xid uses the Mongo Object ID algorithm to generate globally unique ids with a different serialization ([base32hex](https://datatracker.ietf.org/doc/html/rfc4648#page-10)) to make it shorter when transported as a string:
https://docs.mongodb.org/manual/reference/object-id/
- 4-byte value representing the seconds since the Unix epoch,
@@ -13,7 +13,7 @@ https://docs.mongodb.org/manual/reference/object-id/
- 3-byte counter, starting with a random value.
The binary representation of the id is compatible with Mongo 12 bytes Object IDs.
The string representation is using base32 hex (w/o padding) for better space efficiency
The string representation is using [base32hex](https://datatracker.ietf.org/doc/html/rfc4648#page-10) (w/o padding) for better space efficiency
when stored in that form (20 bytes). The hex variant of base32 is used to retain the
sortable property of the id.
@@ -71,8 +71,10 @@ References:
- Java port by [0xShamil](https://github.com/0xShamil/): https://github.com/0xShamil/java-xid
- Dart port by [Peter Bwire](https://github.com/pitabwire): https://pub.dev/packages/xid
- PostgreSQL port by [Rasmus Holm](https://github.com/crholm): https://github.com/modfin/pg-xid
- Swift port by [Uditha Atukorala](https://github.com/uditha-atukorala): https://github.com/uditha-atukorala/swift-xid
- C++ port by [Uditha Atukorala](https://github.com/uditha-atukorala): https://github.com/uditha-atukorala/libxid
- Swift port by [Uditha Atukorala](https://github.com/uatuko): https://github.com/uatuko/swift-xid
- C++ port by [Uditha Atukorala](https://github.com/uatuko): https://github.com/uatuko/libxid
- Typescript & Javascript port by [Yiwen AI](https://github.com/yiwen-ai): https://github.com/yiwen-ai/xid-ts
- Gleam port by [Alexandre Del Vecchio](https://github.com/defgenx): https://github.com/defgenx/gxid
## Install
+27 -2
View File
@@ -2,8 +2,33 @@
package xid
import "syscall"
import (
"errors"
"os/exec"
"strings"
)
func readPlatformMachineID() (string, error) {
return syscall.Sysctl("kern.uuid")
ioreg, err := exec.LookPath("ioreg")
if err != nil {
return "", err
}
cmd := exec.Command(ioreg, "-rd1", "-c", "IOPlatformExpertDevice")
out, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
for _, line := range strings.Split(string(out), "\n") {
if strings.Contains(line, "IOPlatformUUID") {
parts := strings.SplitAfter(line, `" = "`)
if len(parts) == 2 {
uuid := strings.TrimRight(parts[1], `"`)
return strings.ToLower(uuid), nil
}
}
}
return "", errors.New("cannot find host id")
}
+16 -4
View File
@@ -11,11 +11,17 @@ import (
func readPlatformMachineID() (string, error) {
// source: https://github.com/shirou/gopsutil/blob/master/host/host_syscall.go
var h syscall.Handle
err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, syscall.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, syscall.KEY_READ|syscall.KEY_WOW64_64KEY, &h)
regKeyCryptoPtr, err := syscall.UTF16PtrFromString(`SOFTWARE\Microsoft\Cryptography`)
if err != nil {
return "", fmt.Errorf(`error reading registry key "SOFTWARE\Microsoft\Cryptography": %w`, err)
}
err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, regKeyCryptoPtr, 0, syscall.KEY_READ|syscall.KEY_WOW64_64KEY, &h)
if err != nil {
return "", err
}
defer syscall.RegCloseKey(h)
defer func() { _ = syscall.RegCloseKey(h) }()
const syscallRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
const uuidLen = 36
@@ -23,9 +29,15 @@ func readPlatformMachineID() (string, error) {
var regBuf [syscallRegBufLen]uint16
bufLen := uint32(syscallRegBufLen)
var valType uint32
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
mGuidPtr, err := syscall.UTF16PtrFromString(`MachineGuid`)
if err != nil {
return "", err
return "", fmt.Errorf("error reading machine GUID: %w", err)
}
err = syscall.RegQueryValueEx(h, mGuidPtr, nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
if err != nil {
return "", fmt.Errorf("error parsing ")
}
hostID := syscall.UTF16ToString(regBuf[:])
+6 -7
View File
@@ -54,7 +54,6 @@ import (
"sort"
"sync/atomic"
"time"
"unsafe"
)
// Code inspired from mgo/bson ObjectId
@@ -172,7 +171,7 @@ func FromString(id string) (ID, error) {
func (id ID) String() string {
text := make([]byte, encodedLen)
encode(text, id[:])
return *(*string)(unsafe.Pointer(&text))
return string(text)
}
// Encode encodes the id using base32 encoding, writing 20 bytes to dst and return it.
@@ -206,23 +205,23 @@ func encode(dst, id []byte) {
dst[19] = encoding[(id[11]<<4)&0x1F]
dst[18] = encoding[(id[11]>>1)&0x1F]
dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F]
dst[17] = encoding[(id[11]>>6)|(id[10]<<2)&0x1F]
dst[16] = encoding[id[10]>>3]
dst[15] = encoding[id[9]&0x1F]
dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F]
dst[13] = encoding[(id[8]>>2)&0x1F]
dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F]
dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F]
dst[11] = encoding[(id[7]>>4)|(id[6]<<4)&0x1F]
dst[10] = encoding[(id[6]>>1)&0x1F]
dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F]
dst[9] = encoding[(id[6]>>6)|(id[5]<<2)&0x1F]
dst[8] = encoding[id[5]>>3]
dst[7] = encoding[id[4]&0x1F]
dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F]
dst[5] = encoding[(id[3]>>2)&0x1F]
dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F]
dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F]
dst[3] = encoding[(id[2]>>4)|(id[1]<<4)&0x1F]
dst[2] = encoding[(id[1]>>1)&0x1F]
dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F]
dst[1] = encoding[(id[1]>>6)|(id[0]<<2)&0x1F]
dst[0] = encoding[id[0]>>3]
}
+1
View File
@@ -1,3 +1,4 @@
*.test
*~
.idea/
/vendor/
+9 -1
View File
@@ -19,11 +19,15 @@ type EventAuthorization struct {
IsEnterpriseInstall bool `json:"is_enterprise_install"`
}
// ListEventAuthorizations lists authed users and teams for the given event_context.
// You must provide an app-level token to the client using OptionAppLevelToken.
// For more details, see ListEventAuthorizationsContext documentation.
func (api *Client) ListEventAuthorizations(eventContext string) ([]EventAuthorization, error) {
return api.ListEventAuthorizationsContext(context.Background(), eventContext)
}
// ListEventAuthorizationsContext lists authed users and teams for the given event_context. You must provide an app-level token to the client using OptionAppLevelToken. More info: https://api.slack.com/methods/apps.event.authorizations.list
// ListEventAuthorizationsContext lists authed users and teams for the given event_context with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.event.authorizations.list
func (api *Client) ListEventAuthorizationsContext(ctx context.Context, eventContext string) ([]EventAuthorization, error) {
resp := &listEventAuthorizationsResponse{}
@@ -43,10 +47,14 @@ func (api *Client) ListEventAuthorizationsContext(ctx context.Context, eventCont
return resp.Authorizations, nil
}
// UninstallApp uninstalls your app from a workspace.
// For more details, see UninstallAppContext documentation.
func (api *Client) UninstallApp(clientID, clientSecret string) error {
return api.UninstallAppContext(context.Background(), clientID, clientSecret)
}
// UninstallAppContext uninstalls your app from a workspace with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.uninstall
func (api *Client) UninstallAppContext(ctx context.Context, clientID, clientSecret string) error {
values := url.Values{
"client_id": {clientID},
+7 -4
View File
@@ -22,12 +22,14 @@ func (api *Client) authRequest(ctx context.Context, path string, values url.Valu
return response, response.Err()
}
// SendAuthRevoke will send a revocation for our token
// SendAuthRevoke will send a revocation for our token.
// For more details, see SendAuthRevokeContext documentation.
func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) {
return api.SendAuthRevokeContext(context.Background(), token)
}
// SendAuthRevokeContext will send a revocation request for our token to api.revoke with context
// SendAuthRevokeContext will send a revocation request for our token to api.revoke with a custom context.
// Slack API docs: https://api.slack.com/methods/auth.revoke
func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) {
if token == "" {
token = api.token
@@ -50,12 +52,13 @@ type ListTeamsParameters struct {
}
// ListTeams returns all workspaces a token can access.
// More info: https://api.slack.com/methods/admin.teams.list
// For more details, see ListTeamsContext documentation.
func (api *Client) ListTeams(params ListTeamsParameters) ([]Team, string, error) {
return api.ListTeamsContext(context.Background(), params)
}
// ListTeams returns all workspaces a token can access with a custom context.
// ListTeamsContext returns all workspaces a token can access with a custom context.
// Slack API docs: https://api.slack.com/methods/auth.teams.list
func (api *Client) ListTeamsContext(ctx context.Context, params ListTeamsParameters) ([]Team, string, error) {
values := url.Values{
"token": {api.token},
+15 -5
View File
@@ -4,11 +4,21 @@ package slack
//
// More Information: https://api.slack.com/reference/messaging/blocks#image
type ImageBlock struct {
Type MessageBlockType `json:"type"`
ImageURL string `json:"image_url"`
AltText string `json:"alt_text"`
BlockID string `json:"block_id,omitempty"`
Title *TextBlockObject `json:"title,omitempty"`
Type MessageBlockType `json:"type"`
ImageURL string `json:"image_url,omitempty"`
AltText string `json:"alt_text"`
BlockID string `json:"block_id,omitempty"`
Title *TextBlockObject `json:"title,omitempty"`
SlackFile *SlackFileObject `json:"slack_file,omitempty"`
}
// SlackFileObject Defines an object containing Slack file information to be used in an
// image block or image element.
//
// More Information: https://api.slack.com/reference/block-kit/composition-objects#slack_file
type SlackFileObject struct {
ID string `json:"id,omitempty"`
URL string `json:"url,omitempty"`
}
// BlockType returns the type of the block
+10
View File
@@ -147,6 +147,16 @@ func (s TextBlockObject) Validate() error {
return errors.New("emoji cannot be true in mrkdown")
}
// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) == 0 {
return errors.New("text must have a minimum length of 1")
}
// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) > 3000 {
return errors.New("text cannot be longer than 3000 characters")
}
return nil
}
+14 -4
View File
@@ -55,12 +55,14 @@ type listBookmarksResponse struct {
SlackResponse
}
// AddBookmark adds a bookmark in a channel
// AddBookmark adds a bookmark in a channel.
// For more details, see AddBookmarkContext documentation.
func (api *Client) AddBookmark(channelID string, params AddBookmarkParameters) (Bookmark, error) {
return api.AddBookmarkContext(context.Background(), channelID, params)
}
// AddBookmarkContext adds a bookmark in a channel with a custom context
// AddBookmarkContext adds a bookmark in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/bookmarks.add
func (api *Client) AddBookmarkContext(ctx context.Context, channelID string, params AddBookmarkParameters) (Bookmark, error) {
values := url.Values{
"channel_id": {channelID},
@@ -89,12 +91,14 @@ func (api *Client) AddBookmarkContext(ctx context.Context, channelID string, par
return response.Bookmark, response.Err()
}
// RemoveBookmark removes a bookmark from a channel
// RemoveBookmark removes a bookmark from a channel.
// For more details, see RemoveBookmarkContext documentation.
func (api *Client) RemoveBookmark(channelID, bookmarkID string) error {
return api.RemoveBookmarkContext(context.Background(), channelID, bookmarkID)
}
// RemoveBookmarkContext removes a bookmark from a channel with a custom context
// RemoveBookmarkContext removes a bookmark from a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/bookmarks.remove
func (api *Client) RemoveBookmarkContext(ctx context.Context, channelID, bookmarkID string) error {
values := url.Values{
"channel_id": {channelID},
@@ -111,11 +115,13 @@ func (api *Client) RemoveBookmarkContext(ctx context.Context, channelID, bookmar
}
// ListBookmarks returns all bookmarks for a channel.
// For more details, see ListBookmarksContext documentation.
func (api *Client) ListBookmarks(channelID string) ([]Bookmark, error) {
return api.ListBookmarksContext(context.Background(), channelID)
}
// ListBookmarksContext returns all bookmarks for a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/bookmarks.edit
func (api *Client) ListBookmarksContext(ctx context.Context, channelID string) ([]Bookmark, error) {
values := url.Values{
"channel_id": {channelID},
@@ -130,10 +136,14 @@ func (api *Client) ListBookmarksContext(ctx context.Context, channelID string) (
return response.Bookmarks, response.Err()
}
// EditBookmark edits a bookmark in a channel.
// For more details, see EditBookmarkContext documentation.
func (api *Client) EditBookmark(channelID, bookmarkID string, params EditBookmarkParameters) (Bookmark, error) {
return api.EditBookmarkContext(context.Background(), channelID, bookmarkID, params)
}
// EditBookmarkContext edits a bookmark in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/bookmarks.edit
func (api *Client) EditBookmarkContext(ctx context.Context, channelID, bookmarkID string, params EditBookmarkParameters) (Bookmark, error) {
values := url.Values{
"channel_id": {channelID},
+18 -7
View File
@@ -35,19 +35,30 @@ func (api *Client) botRequest(ctx context.Context, path string, values url.Value
return response, nil
}
// GetBotInfo will retrieve the complete bot information
func (api *Client) GetBotInfo(bot string) (*Bot, error) {
return api.GetBotInfoContext(context.Background(), bot)
type GetBotInfoParameters struct {
Bot string
TeamID string
}
// GetBotInfoContext will retrieve the complete bot information using a custom context
func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, error) {
// GetBotInfo will retrieve the complete bot information.
// For more details, see GetBotInfoContext documentation.
func (api *Client) GetBotInfo(parameters GetBotInfoParameters) (*Bot, error) {
return api.GetBotInfoContext(context.Background(), parameters)
}
// GetBotInfoContext will retrieve the complete bot information using a custom context.
// Slack API docs: https://api.slack.com/methods/bots.info
func (api *Client) GetBotInfoContext(ctx context.Context, parameters GetBotInfoParameters) (*Bot, error) {
values := url.Values{
"token": {api.token},
}
if bot != "" {
values.Add("bot", bot)
if parameters.Bot != "" {
values.Add("bot", parameters.Bot)
}
if parameters.TeamID != "" {
values.Add("team_id", parameters.TeamID)
}
response, err := api.botRequest(ctx, "bots.info", values)
+5 -4
View File
@@ -19,10 +19,11 @@ type channelResponseFull struct {
// Channel contains information about the channel
type Channel struct {
GroupConversation
IsChannel bool `json:"is_channel"`
IsGeneral bool `json:"is_general"`
IsMember bool `json:"is_member"`
Locale string `json:"locale"`
IsChannel bool `json:"is_channel"`
IsGeneral bool `json:"is_general"`
IsMember bool `json:"is_member"`
Locale string `json:"locale"`
Properties *Properties `json:"properties"`
}
func (api *Client) channelRequest(ctx context.Context, path string, values url.Values) (*channelResponseFull, error) {
+56 -35
View File
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"io"
"net/http"
"net/url"
"regexp"
@@ -37,7 +37,7 @@ type chatResponseFull struct {
SlackResponse
}
// getMessageTimestamp will inspect the `chatResponseFull` to ruturn a timestamp value
// getMessageTimestamp will inspect the `chatResponseFull` to return a timestamp value
// in `chat.postMessage` its under `ts`
// in `chat.postEphemeral` its under `message_ts`
func (c chatResponseFull) getMessageTimestamp() string {
@@ -88,12 +88,14 @@ func NewPostMessageParameters() PostMessageParameters {
}
}
// DeleteMessage deletes a message in a channel
// DeleteMessage deletes a message in a channel.
// For more details, see DeleteMessageContext documentation.
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
return api.DeleteMessageContext(context.Background(), channel, messageTimestamp)
}
// DeleteMessageContext deletes a message in a channel with a custom context
// DeleteMessageContext deletes a message in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.delete
func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) {
respChannel, respTimestamp, _, err := api.SendMessageContext(
ctx,
@@ -106,13 +108,13 @@ func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTim
// ScheduleMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
// For more details, see ScheduleMessageContext documentation.
func (api *Client) ScheduleMessage(channelID, postAt string, options ...MsgOption) (string, string, error) {
return api.ScheduleMessageContext(context.Background(), channelID, postAt, options...)
}
// ScheduleMessageContext sends a message to a channel with a custom context
//
// For more details, see ScheduleMessage documentation.
// ScheduleMessageContext sends a message to a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.scheduleMessage
func (api *Client) ScheduleMessageContext(ctx context.Context, channelID, postAt string, options ...MsgOption) (string, string, error) {
respChannel, respTimestamp, _, err := api.SendMessageContext(
ctx,
@@ -126,12 +128,13 @@ func (api *Client) ScheduleMessageContext(ctx context.Context, channelID, postAt
// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
// For more details, see PostMessageContext documentation.
func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error) {
return api.PostMessageContext(context.Background(), channelID, options...)
}
// PostMessageContext sends a message to a channel with a custom context
// For more details, see PostMessage documentation.
// PostMessageContext sends a message to a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.postMessage
func (api *Client) PostMessageContext(ctx context.Context, channelID string, options ...MsgOption) (string, string, error) {
respChannel, respTimestamp, _, err := api.SendMessageContext(
ctx,
@@ -145,12 +148,13 @@ func (api *Client) PostMessageContext(ctx context.Context, channelID string, opt
// PostEphemeral sends an ephemeral message to a user in a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
// For more details, see PostEphemeralContext documentation.
func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) (string, error) {
return api.PostEphemeralContext(context.Background(), channelID, userID, options...)
}
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
// For more details, see PostEphemeral documentation
// PostEphemeralContext sends an ephemeral message to a user in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.postEphemeral
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
_, timestamp, _, err = api.SendMessageContext(
ctx,
@@ -161,12 +165,14 @@ func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID s
return timestamp, err
}
// UpdateMessage updates a message in a channel
// UpdateMessage updates a message in a channel.
// For more details, see UpdateMessageContext documentation.
func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
return api.UpdateMessageContext(context.Background(), channelID, timestamp, options...)
}
// UpdateMessageContext updates a message in a channel
// UpdateMessageContext updates a message in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.update
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(
ctx,
@@ -176,38 +182,38 @@ func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestam
)
}
// UnfurlMessage unfurls a message in a channel
// UnfurlMessage unfurls a message in a channel.
// For more details, see UnfurlMessageContext documentation.
func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) {
return api.UnfurlMessageContext(context.Background(), channelID, timestamp, unfurls, options...)
}
// UnfurlMessageContext unfurls a message in a channel with a custom context
// UnfurlMessageContext unfurls a message in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.unfurl
func (api *Client) UnfurlMessageContext(ctx context.Context, channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(ctx, channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
}
// UnfurlMessageWithAuthURL sends an unfurl request containing an
// authentication URL.
// For more details see:
// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
// UnfurlMessageWithAuthURL sends an unfurl request containing an authentication URL.
// For more details, see UnfurlMessageWithAuthURLContext documentation.
func (api *Client) UnfurlMessageWithAuthURL(channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
return api.UnfurlMessageWithAuthURLContext(context.Background(), channelID, timestamp, userAuthURL, options...)
}
// UnfurlMessageWithAuthURLContext sends an unfurl request containing an
// authentication URL.
// For more details see:
// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
// UnfurlMessageWithAuthURLContext sends an unfurl request containing an authentication URL with a custom context.
// For more details see: https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
func (api *Client) UnfurlMessageWithAuthURLContext(ctx context.Context, channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(ctx, channelID, MsgOptionUnfurlAuthURL(timestamp, userAuthURL), MsgOptionCompose(options...))
}
// SendMessage more flexible method for configuring messages.
// For more details, see SendMessageContext documentation.
func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(context.Background(), channel, options...)
}
// SendMessageContext more flexible method for configuring messages with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.postMessage
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (_channel string, _timestamp string, _text string, err error) {
var (
req *http.Request
@@ -220,11 +226,11 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
}
if api.Debug() {
reqBody, err := ioutil.ReadAll(req.Body)
reqBody, err := io.ReadAll(req.Body)
if err != nil {
return "", "", "", err
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
req.Body = io.NopCloser(bytes.NewBuffer(reqBody))
api.Debugf("Sending request: %s", redactToken(reqBody))
}
@@ -696,6 +702,14 @@ func MsgOptionMetadata(metadata SlackMetadata) MsgOption {
}
}
// MsgOptionLinkNames finds and links user groups. Does not support linking individual users
func MsgOptionLinkNames(linkName bool) MsgOption {
return func(config *sendConfig) error {
config.values.Set("link_names", strconv.FormatBool(linkName))
return nil
}
}
// UnsafeMsgOptionEndpoint deliver the message to the specified endpoint.
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this Option
// will be supported by the library, it is subject to change without notice that
@@ -763,22 +777,21 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
}
}
// PermalinkParameters are the parameters required to get a permalink to a
// message. Slack documentation can be found here:
// https://api.slack.com/methods/chat.getPermalink
// PermalinkParameters are the parameters required to get a permalink to a message.
type PermalinkParameters struct {
Channel string
Ts string
}
// GetPermalink returns the permalink for a message. It takes
// PermalinkParameters and returns a string containing the permalink. It
// returns an error if unable to retrieve the permalink.
// GetPermalink returns the permalink for a message. It takes PermalinkParameters and returns a string containing the
// permalink. It returns an error if unable to retrieve the permalink.
// For more details, see GetPermalinkContext documentation.
func (api *Client) GetPermalink(params *PermalinkParameters) (string, error) {
return api.GetPermalinkContext(context.Background(), params)
}
// GetPermalinkContext returns the permalink for a message using a custom context.
// Slack API docs: https://api.slack.com/methods/chat.getPermalink
func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkParameters) (string, error) {
values := url.Values{
"channel": {params.Channel},
@@ -799,18 +812,21 @@ func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkPar
type GetScheduledMessagesParameters struct {
Channel string
TeamID string
Cursor string
Latest string
Limit int
Oldest string
}
// GetScheduledMessages returns the list of scheduled messages based on params
// GetScheduledMessages returns the list of scheduled messages based on params.
// For more details, see GetScheduledMessagesContext documentation.
func (api *Client) GetScheduledMessages(params *GetScheduledMessagesParameters) (channels []ScheduledMessage, nextCursor string, err error) {
return api.GetScheduledMessagesContext(context.Background(), params)
}
// GetScheduledMessagesContext returns the list of scheduled messages in a Slack team with a custom context
// GetScheduledMessagesContext returns the list of scheduled messages based on params with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.getScheduledMessages.list
func (api *Client) GetScheduledMessagesContext(ctx context.Context, params *GetScheduledMessagesParameters) (channels []ScheduledMessage, nextCursor string, err error) {
values := url.Values{
"token": {api.token},
@@ -818,6 +834,9 @@ func (api *Client) GetScheduledMessagesContext(ctx context.Context, params *GetS
if params.Channel != "" {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Cursor != "" {
values.Add("cursor", params.Cursor)
}
@@ -850,12 +869,14 @@ type DeleteScheduledMessageParameters struct {
AsUser bool
}
// DeleteScheduledMessage returns the list of scheduled messages based on params
// DeleteScheduledMessage deletes a pending scheduled message.
// For more details, see DeleteScheduledMessageContext documentation.
func (api *Client) DeleteScheduledMessage(params *DeleteScheduledMessageParameters) (bool, error) {
return api.DeleteScheduledMessageContext(context.Background(), params)
}
// DeleteScheduledMessageContext returns the list of scheduled messages in a Slack team with a custom context
// DeleteScheduledMessageContext deletes a pending scheduled message with a custom context.
// Slack API docs: https://api.slack.com/methods/chat.deleteScheduledMessage
func (api *Client) DeleteScheduledMessageContext(ctx context.Context, params *DeleteScheduledMessageParameters) (bool, error) {
values := url.Values{
"token": {api.token},
+96 -41
View File
@@ -25,6 +25,7 @@ type Conversation struct {
IsGlobalShared bool `json:"is_global_shared"`
IsPendingExtShared bool `json:"is_pending_ext_shared"`
IsPrivate bool `json:"is_private"`
IsReadOnly bool `json:"is_read_only"`
IsMpIM bool `json:"is_mpim"`
Unlinked int `json:"unlinked"`
NameNormalized string `json:"name_normalized"`
@@ -64,6 +65,17 @@ type Purpose struct {
LastSet JSONTime `json:"last_set"`
}
// Properties contains the Canvas associated to the channel.
type Properties struct {
Canvas Canvas `json:"canvas"`
}
type Canvas struct {
FileId string `json:"file_id"`
IsEmpty bool `json:"is_empty"`
QuipThreadId string `json:"quip_thread_id"`
}
type GetUsersInConversationParameters struct {
ChannelID string
Cursor string
@@ -83,12 +95,14 @@ type responseMetaData struct {
NextCursor string `json:"next_cursor"`
}
// GetUsersInConversation returns the list of users in a conversation
// GetUsersInConversation returns the list of users in a conversation.
// For more details, see GetUsersInConversationContext documentation.
func (api *Client) GetUsersInConversation(params *GetUsersInConversationParameters) ([]string, string, error) {
return api.GetUsersInConversationContext(context.Background(), params)
}
// GetUsersInConversationContext returns the list of users in a conversation with a custom context
// GetUsersInConversationContext returns the list of users in a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.members
func (api *Client) GetUsersInConversationContext(ctx context.Context, params *GetUsersInConversationParameters) ([]string, string, error) {
values := url.Values{
"token": {api.token},
@@ -118,12 +132,14 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge
return response.Members, response.ResponseMetaData.NextCursor, nil
}
// GetConversationsForUser returns the list conversations for a given user
// GetConversationsForUser returns the list conversations for a given user.
// For more details, see GetConversationsForUserContext documentation.
func (api *Client) GetConversationsForUser(params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
return api.GetConversationsForUserContext(context.Background(), params)
}
// GetConversationsForUserContext returns the list conversations for a given user with a custom context
// Slack API docs: https://api.slack.com/methods/users.conversations
func (api *Client) GetConversationsForUserContext(ctx context.Context, params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
values := url.Values{
"token": {api.token},
@@ -160,12 +176,14 @@ func (api *Client) GetConversationsForUserContext(ctx context.Context, params *G
return response.Channels, response.ResponseMetaData.NextCursor, response.Err()
}
// ArchiveConversation archives a conversation
// ArchiveConversation archives a conversation.
// For more details, see ArchiveConversationContext documentation.
func (api *Client) ArchiveConversation(channelID string) error {
return api.ArchiveConversationContext(context.Background(), channelID)
}
// ArchiveConversationContext archives a conversation with a custom context
// ArchiveConversationContext archives a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.archive
func (api *Client) ArchiveConversationContext(ctx context.Context, channelID string) error {
values := url.Values{
"token": {api.token},
@@ -181,12 +199,14 @@ func (api *Client) ArchiveConversationContext(ctx context.Context, channelID str
return response.Err()
}
// UnArchiveConversation reverses conversation archival
// UnArchiveConversation reverses conversation archival.
// For more details, see UnArchiveConversationContext documentation.
func (api *Client) UnArchiveConversation(channelID string) error {
return api.UnArchiveConversationContext(context.Background(), channelID)
}
// UnArchiveConversationContext reverses conversation archival with a custom context
// UnArchiveConversationContext reverses conversation archival with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.unarchive
func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID string) error {
values := url.Values{
"token": {api.token},
@@ -201,12 +221,14 @@ func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID s
return response.Err()
}
// SetTopicOfConversation sets the topic for a conversation
// SetTopicOfConversation sets the topic for a conversation.
// For more details, see SetTopicOfConversationContext documentation.
func (api *Client) SetTopicOfConversation(channelID, topic string) (*Channel, error) {
return api.SetTopicOfConversationContext(context.Background(), channelID, topic)
}
// SetTopicOfConversationContext sets the topic for a conversation with a custom context
// SetTopicOfConversationContext sets the topic for a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.setTopic
func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID, topic string) (*Channel, error) {
values := url.Values{
"token": {api.token},
@@ -225,12 +247,14 @@ func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID,
return response.Channel, response.Err()
}
// SetPurposeOfConversation sets the purpose for a conversation
// SetPurposeOfConversation sets the purpose for a conversation.
// For more details, see SetPurposeOfConversationContext documentation.
func (api *Client) SetPurposeOfConversation(channelID, purpose string) (*Channel, error) {
return api.SetPurposeOfConversationContext(context.Background(), channelID, purpose)
}
// SetPurposeOfConversationContext sets the purpose for a conversation with a custom context
// SetPurposeOfConversationContext sets the purpose for a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.setPurpose
func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelID, purpose string) (*Channel, error) {
values := url.Values{
"token": {api.token},
@@ -250,12 +274,14 @@ func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelI
return response.Channel, response.Err()
}
// RenameConversation renames a conversation
// RenameConversation renames a conversation.
// For more details, see RenameConversationContext documentation.
func (api *Client) RenameConversation(channelID, channelName string) (*Channel, error) {
return api.RenameConversationContext(context.Background(), channelID, channelName)
}
// RenameConversationContext renames a conversation with a custom context
// RenameConversationContext renames a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.rename
func (api *Client) RenameConversationContext(ctx context.Context, channelID, channelName string) (*Channel, error) {
values := url.Values{
"token": {api.token},
@@ -275,12 +301,14 @@ func (api *Client) RenameConversationContext(ctx context.Context, channelID, cha
return response.Channel, response.Err()
}
// InviteUsersToConversation invites users to a channel
// InviteUsersToConversation invites users to a channel.
// For more details, see InviteUsersToConversation documentation.
func (api *Client) InviteUsersToConversation(channelID string, users ...string) (*Channel, error) {
return api.InviteUsersToConversationContext(context.Background(), channelID, users...)
}
// InviteUsersToConversationContext invites users to a channel with a custom context
// InviteUsersToConversationContext invites users to a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.invite
func (api *Client) InviteUsersToConversationContext(ctx context.Context, channelID string, users ...string) (*Channel, error) {
values := url.Values{
"token": {api.token},
@@ -300,22 +328,26 @@ func (api *Client) InviteUsersToConversationContext(ctx context.Context, channel
return response.Channel, response.Err()
}
// InviteSharedEmailsToConversation invites users to a shared channels by email
// InviteSharedEmailsToConversation invites users to a shared channels by email.
// For more details, see InviteSharedEmailsToConversationContext documentation.
func (api *Client) InviteSharedEmailsToConversation(channelID string, emails ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(context.Background(), channelID, emails, nil)
}
// InviteSharedEmailsToConversationContext invites users to a shared channels by email using context
// InviteSharedEmailsToConversationContext invites users to a shared channels by email using context.
// For more details, see inviteSharedToConversationHelper documentation.
func (api *Client) InviteSharedEmailsToConversationContext(ctx context.Context, channelID string, emails ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(ctx, channelID, emails, nil)
}
// InviteSharedUserIDsToConversation invites users to a shared channels by user id
// InviteSharedUserIDsToConversation invites users to a shared channels by user id.
// For more details, see InviteSharedUserIDsToConversationContext documentation.
func (api *Client) InviteSharedUserIDsToConversation(channelID string, userIDs ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(context.Background(), channelID, nil, userIDs)
}
// InviteSharedUserIDsToConversationContext invites users to a shared channels by user id with context
// InviteSharedUserIDsToConversationContext invites users to a shared channels by user id with context.
// For more details, see inviteSharedToConversationHelper documentation.
func (api *Client) InviteSharedUserIDsToConversationContext(ctx context.Context, channelID string, userIDs ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(ctx, channelID, nil, userIDs)
}
@@ -323,6 +355,7 @@ func (api *Client) InviteSharedUserIDsToConversationContext(ctx context.Context,
// inviteSharedToConversationHelper invites emails or userIDs to a channel with a custom context.
// This is a helper function for InviteSharedEmailsToConversation and InviteSharedUserIDsToConversation.
// It accepts either emails or userIDs, but not both.
// Slack API docs: https://api.slack.com/methods/conversations.inviteShared
func (api *Client) inviteSharedToConversationHelper(ctx context.Context, channelID string, emails []string, userIDs []string) (string, bool, error) {
values := url.Values{
"token": {api.token},
@@ -347,12 +380,14 @@ func (api *Client) inviteSharedToConversationHelper(ctx context.Context, channel
return response.InviteID, response.IsLegacySharedChannel, response.Err()
}
// KickUserFromConversation removes a user from a conversation
// KickUserFromConversation removes a user from a conversation.
// For more details, see KickUserFromConversationContext documentation.
func (api *Client) KickUserFromConversation(channelID string, user string) error {
return api.KickUserFromConversationContext(context.Background(), channelID, user)
}
// KickUserFromConversationContext removes a user from a conversation with a custom context
// KickUserFromConversationContext removes a user from a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.kick
func (api *Client) KickUserFromConversationContext(ctx context.Context, channelID string, user string) error {
values := url.Values{
"token": {api.token},
@@ -369,12 +404,14 @@ func (api *Client) KickUserFromConversationContext(ctx context.Context, channelI
return response.Err()
}
// CloseConversation closes a direct message or multi-person direct message
// CloseConversation closes a direct message or multi-person direct message.
// For more details, see CloseConversationContext documentation.
func (api *Client) CloseConversation(channelID string) (noOp bool, alreadyClosed bool, err error) {
return api.CloseConversationContext(context.Background(), channelID)
}
// CloseConversationContext closes a direct message or multi-person direct message with a custom context
// CloseConversationContext closes a direct message or multi-person direct message with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.close
func (api *Client) CloseConversationContext(ctx context.Context, channelID string) (noOp bool, alreadyClosed bool, err error) {
values := url.Values{
"token": {api.token},
@@ -400,12 +437,14 @@ type CreateConversationParams struct {
TeamID string
}
// CreateConversation initiates a public or private channel-based conversation
// CreateConversation initiates a public or private channel-based conversation.
// For more details, see CreateConversationContext documentation.
func (api *Client) CreateConversation(params CreateConversationParams) (*Channel, error) {
return api.CreateConversationContext(context.Background(), params)
}
// CreateConversationContext initiates a public or private channel-based conversation with a custom context
// CreateConversationContext initiates a public or private channel-based conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.create
func (api *Client) CreateConversationContext(ctx context.Context, params CreateConversationParams) (*Channel, error) {
values := url.Values{
"token": {api.token},
@@ -430,12 +469,14 @@ type GetConversationInfoInput struct {
IncludeNumMembers bool
}
// GetConversationInfo retrieves information about a conversation
// GetConversationInfo retrieves information about a conversation.
// For more details, see GetConversationInfoContext documentation.
func (api *Client) GetConversationInfo(input *GetConversationInfoInput) (*Channel, error) {
return api.GetConversationInfoContext(context.Background(), input)
}
// GetConversationInfoContext retrieves information about a conversation with a custom context
// GetConversationInfoContext retrieves information about a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.info
func (api *Client) GetConversationInfoContext(ctx context.Context, input *GetConversationInfoInput) (*Channel, error) {
if input == nil {
return nil, errors.New("GetConversationInfoInput must not be nil")
@@ -459,12 +500,14 @@ func (api *Client) GetConversationInfoContext(ctx context.Context, input *GetCon
return &response.Channel, response.Err()
}
// LeaveConversation leaves a conversation
// LeaveConversation leaves a conversation.
// For more details, see LeaveConversationContext documentation.
func (api *Client) LeaveConversation(channelID string) (bool, error) {
return api.LeaveConversationContext(context.Background(), channelID)
}
// LeaveConversationContext leaves a conversation with a custom context
// LeaveConversationContext leaves a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.leave
func (api *Client) LeaveConversationContext(ctx context.Context, channelID string) (bool, error) {
values := url.Values{
"token": {api.token},
@@ -490,12 +533,14 @@ type GetConversationRepliesParameters struct {
IncludeAllMetadata bool
}
// GetConversationReplies retrieves a thread of messages posted to a conversation
// GetConversationReplies retrieves a thread of messages posted to a conversation.
// For more details, see GetConversationRepliesContext documentation.
func (api *Client) GetConversationReplies(params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
return api.GetConversationRepliesContext(context.Background(), params)
}
// GetConversationRepliesContext retrieves a thread of messages posted to a conversation with a custom context
// GetConversationRepliesContext retrieves a thread of messages posted to a conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.replies
func (api *Client) GetConversationRepliesContext(ctx context.Context, params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
values := url.Values{
"token": {api.token},
@@ -549,12 +594,14 @@ type GetConversationsParameters struct {
TeamID string
}
// GetConversations returns the list of channels in a Slack team
// GetConversations returns the list of channels in a Slack team.
// For more details, see GetConversationsContext documentation.
func (api *Client) GetConversations(params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
return api.GetConversationsContext(context.Background(), params)
}
// GetConversationsContext returns the list of channels in a Slack team with a custom context
// GetConversationsContext returns the list of channels in a Slack team with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.list
func (api *Client) GetConversationsContext(ctx context.Context, params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
values := url.Values{
"token": {api.token},
@@ -595,12 +642,14 @@ type OpenConversationParameters struct {
Users []string
}
// OpenConversation opens or resumes a direct message or multi-person direct message
// OpenConversation opens or resumes a direct message or multi-person direct message.
// For more details, see OpenConversationContext documentation.
func (api *Client) OpenConversation(params *OpenConversationParameters) (*Channel, bool, bool, error) {
return api.OpenConversationContext(context.Background(), params)
}
// OpenConversationContext opens or resumes a direct message or multi-person direct message with a custom context
// OpenConversationContext opens or resumes a direct message or multi-person direct message with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.open
func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConversationParameters) (*Channel, bool, bool, error) {
values := url.Values{
"token": {api.token},
@@ -627,12 +676,14 @@ func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConv
return response.Channel, response.NoOp, response.AlreadyOpen, response.Err()
}
// JoinConversation joins an existing conversation
// JoinConversation joins an existing conversation.
// For more details, see JoinConversationContext documentation.
func (api *Client) JoinConversation(channelID string) (*Channel, string, []string, error) {
return api.JoinConversationContext(context.Background(), channelID)
}
// JoinConversationContext joins an existing conversation with a custom context
// JoinConversationContext joins an existing conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.join
func (api *Client) JoinConversationContext(ctx context.Context, channelID string) (*Channel, string, []string, error) {
values := url.Values{"token": {api.token}, "channel": {channelID}}
response := struct {
@@ -679,12 +730,14 @@ type GetConversationHistoryResponse struct {
Messages []Message `json:"messages"`
}
// GetConversationHistory joins an existing conversation
// GetConversationHistory joins an existing conversation.
// For more details, see GetConversationHistoryContext documentation.
func (api *Client) GetConversationHistory(params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
return api.GetConversationHistoryContext(context.Background(), params)
}
// GetConversationHistoryContext joins an existing conversation with a custom context
// GetConversationHistoryContext joins an existing conversation with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.history
func (api *Client) GetConversationHistoryContext(ctx context.Context, params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
values := url.Values{"token": {api.token}, "channel": {params.ChannelID}}
if params.Cursor != "" {
@@ -720,12 +773,14 @@ func (api *Client) GetConversationHistoryContext(ctx context.Context, params *Ge
return &response, response.Err()
}
// MarkConversation sets the read mark of a conversation to a specific point
// MarkConversation sets the read mark of a conversation to a specific point.
// For more details, see MarkConversationContext documentation.
func (api *Client) MarkConversation(channel, ts string) (err error) {
return api.MarkConversationContext(context.Background(), channel, ts)
}
// MarkConversationContext sets the read mark of a conversation to a specific point with a custom context
// MarkConversationContext sets the read mark of a conversation to a specific point with a custom context.
// Slack API docs: https://api.slack.com/methods/conversations.mark
func (api *Client) MarkConversationContext(ctx context.Context, channel, ts string) error {
values := url.Values{
"token": {api.token},
+18 -9
View File
@@ -45,12 +45,14 @@ func (api *Client) dndRequest(ctx context.Context, path string, values url.Value
return response, response.Err()
}
// EndDND ends the user's scheduled Do Not Disturb session
// EndDND ends the user's scheduled Do Not Disturb session.
// For more information see the EndDNDContext documentation.
func (api *Client) EndDND() error {
return api.EndDNDContext(context.Background())
}
// EndDNDContext ends the user's scheduled Do Not Disturb session with a custom context
// EndDNDContext ends the user's scheduled Do Not Disturb session with a custom context.
// Slack API docs: https://api.slack.com/methods/dnd.endDnd
func (api *Client) EndDNDContext(ctx context.Context) error {
values := url.Values{
"token": {api.token},
@@ -65,12 +67,14 @@ func (api *Client) EndDNDContext(ctx context.Context) error {
return response.Err()
}
// EndSnooze ends the current user's snooze mode
// EndSnooze ends the current user's snooze mode.
// For more information see the EndSnoozeContext documentation.
func (api *Client) EndSnooze() (*DNDStatus, error) {
return api.EndSnoozeContext(context.Background())
}
// EndSnoozeContext ends the current user's snooze mode with a custom context
// EndSnoozeContext ends the current user's snooze mode with a custom context.
// Slack API docs: https://api.slack.com/methods/dnd.endSnooze
func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
values := url.Values{
"token": {api.token},
@@ -84,11 +88,13 @@ func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
}
// GetDNDInfo provides information about a user's current Do Not Disturb settings.
// For more information see the GetDNDInfoContext documentation.
func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
return api.GetDNDInfoContext(context.Background(), user)
}
// GetDNDInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
// Slack API docs: https://api.slack.com/methods/dnd.info
func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDStatus, error) {
values := url.Values{
"token": {api.token},
@@ -105,11 +111,13 @@ func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDSta
}
// GetDNDTeamInfo provides information about a user's current Do Not Disturb settings.
// For more information see the GetDNDTeamInfoContext documentation.
func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error) {
return api.GetDNDTeamInfoContext(context.Background(), users)
}
// GetDNDTeamInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
// Slack API docs: https://api.slack.com/methods/dnd.teamInfo
func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (map[string]DNDStatus, error) {
values := url.Values{
"token": {api.token},
@@ -128,15 +136,16 @@ func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (m
return response.Users, nil
}
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb
// settings. If a snooze session is not already active for the user, invoking
// this method will begin one for the specified duration.
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb settings.
// For more information see the SetSnoozeContext documentation.
func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
return api.SetSnoozeContext(context.Background(), minutes)
}
// SetSnoozeContext adjusts the snooze duration for a user's Do Not Disturb settings with a custom context.
// For more information see the SetSnooze docs
// SetSnoozeContext adjusts the snooze duration for a user's Do Not Disturb settings.
// If a snooze session is not already active for the user, invoking this method will
// begin one for the specified duration.
// Slack API docs: https://api.slack.com/methods/dnd.setSnooze
func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatus, error) {
values := url.Values{
"token": {api.token},
+4 -2
View File
@@ -10,12 +10,14 @@ type emojiResponseFull struct {
SlackResponse
}
// GetEmoji retrieves all the emojis
// GetEmoji retrieves all the emojis.
// For more details see GetEmojiContext documentation.
func (api *Client) GetEmoji() (map[string]string, error) {
return api.GetEmojiContext(context.Background())
}
// GetEmojiContext retrieves all the emojis with a custom context
// GetEmojiContext retrieves all the emojis with a custom context.
// Slack API docs: https://api.slack.com/methods/emoji.list
func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, error) {
values := url.Values{
"token": {api.token},
+57 -35
View File
@@ -11,7 +11,7 @@ import (
)
const (
// Add here the defaults in the siten
// Add here the defaults in the site
DEFAULT_FILES_USER = ""
DEFAULT_FILES_CHANNEL = ""
DEFAULT_FILES_TS_FROM = 0
@@ -129,6 +129,7 @@ type FileUploadParameters struct {
type GetFilesParameters struct {
User string
Channel string
TeamID string
TimestampFrom JSONTime
TimestampTo JSONTime
Types string
@@ -142,6 +143,7 @@ type ListFilesParameters struct {
Limit int
User string
Channel string
TeamID string
Types string
Cursor string
}
@@ -232,12 +234,14 @@ func (api *Client) fileRequest(ctx context.Context, path string, values url.Valu
return response, response.Err()
}
// GetFileInfo retrieves a file and related comments
// GetFileInfo retrieves a file and related comments.
// For more details, see GetFileInfoContext documentation.
func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) {
return api.GetFileInfoContext(context.Background(), fileID, count, page)
}
// GetFileInfoContext retrieves a file and related comments with a custom context
// GetFileInfoContext retrieves a file and related comments with a custom context.
// Slack API docs: https://api.slack.com/methods/files.info
func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count, page int) (*File, []Comment, *Paging, error) {
values := url.Values{
"token": {api.token},
@@ -253,24 +257,25 @@ func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count,
return &response.File, response.Comments, &response.Paging, nil
}
// GetFile retreives a given file from its private download URL
// GetFile retrieves a given file from its private download URL.
func (api *Client) GetFile(downloadURL string, writer io.Writer) error {
return api.GetFileContext(context.Background(), downloadURL, writer)
}
// GetFileContext retreives a given file from its private download URL with a custom context
//
// GetFileContext retrieves a given file from its private download URL with a custom context.
// For more details, see GetFile documentation.
func (api *Client) GetFileContext(ctx context.Context, downloadURL string, writer io.Writer) error {
return downloadFile(ctx, api.httpclient, api.token, downloadURL, writer, api)
}
// GetFiles retrieves all files according to the parameters given
// GetFiles retrieves all files according to the parameters given.
// For more details, see GetFilesContext documentation.
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
return api.GetFilesContext(context.Background(), params)
}
// GetFilesContext retrieves all files according to the parameters given with a custom context
// GetFilesContext retrieves all files according to the parameters given with a custom context.
// Slack API docs: https://api.slack.com/methods/files.list
func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameters) ([]File, *Paging, error) {
values := url.Values{
"token": {api.token},
@@ -281,6 +286,9 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
if params.Channel != DEFAULT_FILES_CHANNEL {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
values.Add("ts_from", strconv.FormatInt(int64(params.TimestampFrom), 10))
}
@@ -308,13 +316,13 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
}
// ListFiles retrieves all files according to the parameters given. Uses cursor based pagination.
// For more details, see ListFilesContext documentation.
func (api *Client) ListFiles(params ListFilesParameters) ([]File, *ListFilesParameters, error) {
return api.ListFilesContext(context.Background(), params)
}
// ListFilesContext retrieves all files according to the parameters given with a custom context.
//
// For more details, see ListFiles documentation.
// Slack API docs: https://api.slack.com/methods/files.list
func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParameters) ([]File, *ListFilesParameters, error) {
values := url.Values{
"token": {api.token},
@@ -326,6 +334,9 @@ func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParamet
if params.Channel != DEFAULT_FILES_CHANNEL {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Limit != DEFAULT_FILES_COUNT {
values.Add("limit", strconv.Itoa(params.Limit))
}
@@ -343,12 +354,16 @@ func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParamet
return response.Files, &params, nil
}
// UploadFile uploads a file
// UploadFile uploads a file.
// DEPRECATED: Use UploadFileV2 instead. This will stop functioning on March 11, 2025.
// For more details, see: https://api.slack.com/methods/files.upload#markdown
func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
return api.UploadFileContext(context.Background(), params)
}
// UploadFileContext uploads a file and setting a custom context
// UploadFileContext uploads a file and setting a custom context.
// DEPRECATED: Use UploadFileV2Context instead. This will stop functioning on March 11, 2025.
// For more details, see: https://api.slack.com/methods/files.upload#markdown
func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParameters) (file *File, err error) {
// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
// investigation needed, but for now this will do.
@@ -396,12 +411,14 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
return &response.File, response.Err()
}
// DeleteFileComment deletes a file's comment
// DeleteFileComment deletes a file's comment.
// For more details, see DeleteFileCommentContext documentation.
func (api *Client) DeleteFileComment(commentID, fileID string) error {
return api.DeleteFileCommentContext(context.Background(), fileID, commentID)
}
// DeleteFileCommentContext deletes a file's comment with a custom context
// DeleteFileCommentContext deletes a file's comment with a custom context.
// Slack API docs: https://api.slack.com/methods/files.comments.delete
func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, commentID string) (err error) {
if fileID == "" || commentID == "" {
return ErrParametersMissing
@@ -416,12 +433,14 @@ func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, comment
return err
}
// DeleteFile deletes a file
// DeleteFile deletes a file.
// For more details, see DeleteFileContext documentation.
func (api *Client) DeleteFile(fileID string) error {
return api.DeleteFileContext(context.Background(), fileID)
}
// DeleteFileContext deletes a file with a custom context
// DeleteFileContext deletes a file with a custom context.
// Slack API docs: https://api.slack.com/methods/files.delete
func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err error) {
values := url.Values{
"token": {api.token},
@@ -432,12 +451,14 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err er
return err
}
// RevokeFilePublicURL disables public/external sharing for a file
// RevokeFilePublicURL disables public/external sharing for a file.
// For more details, see RevokeFilePublicURLContext documentation.
func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
return api.RevokeFilePublicURLContext(context.Background(), fileID)
}
// RevokeFilePublicURLContext disables public/external sharing for a file with a custom context
// RevokeFilePublicURLContext disables public/external sharing for a file with a custom context.
// Slack API docs: https://api.slack.com/methods/files.revokePublicURL
func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string) (*File, error) {
values := url.Values{
"token": {api.token},
@@ -451,12 +472,14 @@ func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string
return &response.File, nil
}
// ShareFilePublicURL enabled public/external sharing for a file
// ShareFilePublicURL enabled public/external sharing for a file.
// For more details, see ShareFilePublicURLContext documentation.
func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging, error) {
return api.ShareFilePublicURLContext(context.Background(), fileID)
}
// ShareFilePublicURLContext enabled public/external sharing for a file with a custom context
// ShareFilePublicURLContext enabled public/external sharing for a file with a custom context.
// Slack API docs: https://api.slack.com/methods/files.sharedPublicURL
func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string) (*File, []Comment, *Paging, error) {
values := url.Values{
"token": {api.token},
@@ -470,7 +493,7 @@ func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string)
return &response.File, response.Comments, &response.Paging, nil
}
// getUploadURLExternal gets a URL and fileID from slack which can later be used to upload a file
// getUploadURLExternal gets a URL and fileID from slack which can later be used to upload a file.
func (api *Client) getUploadURLExternal(ctx context.Context, params getUploadURLExternalParameters) (*getUploadURLExternalResponse, error) {
values := url.Values{
"token": {api.token},
@@ -496,9 +519,8 @@ func (api *Client) getUploadURLExternal(ctx context.Context, params getUploadURL
func (api *Client) uploadToURL(ctx context.Context, params uploadToURLParameters) (err error) {
values := url.Values{}
if params.Content != "" {
values.Add("content", params.Content)
values.Add("token", api.token)
err = postForm(ctx, api.httpclient, params.UploadURL, values, nil, api)
contentReader := strings.NewReader(params.Content)
err = postWithMultipartResponse(ctx, api.httpclient, params.UploadURL, params.Filename, "file", api.token, values, contentReader, nil, api)
} else if params.File != "" {
err = postLocalWithMultipartResponse(ctx, api.httpclient, params.UploadURL, params.File, "file", api.token, values, nil, api)
} else if params.Reader != nil {
@@ -515,11 +537,13 @@ func (api *Client) completeUploadExternal(ctx context.Context, fileID string, pa
return nil, err
}
values := url.Values{
"token": {api.token},
"files": {string(requestBytes)},
"channel_id": {params.channel},
"token": {api.token},
"files": {string(requestBytes)},
}
if params.channel != "" {
values.Add("channel_id", params.channel)
}
if params.initialComment != "" {
values.Add("initial_comment", params.initialComment)
}
@@ -537,18 +561,18 @@ func (api *Client) completeUploadExternal(ctx context.Context, fileID string, pa
return response, nil
}
// UploadFileV2 uploads file to a given slack channel using 3 steps -
// 1. Get an upload URL using files.getUploadURLExternal API
// 2. Send the file as a post to the URL provided by slack
// 3. Complete the upload and share it to the specified channel using files.completeUploadExternal
// UploadFileV2 uploads file to a given slack channel using 3 steps.
// For more details, see UploadFileV2Context documentation.
func (api *Client) UploadFileV2(params UploadFileV2Parameters) (*FileSummary, error) {
return api.UploadFileV2Context(context.Background(), params)
}
// UploadFileV2 uploads file to a given slack channel using 3 steps with a custom context -
// UploadFileV2Context uploads file to a given slack channel using 3 steps -
// 1. Get an upload URL using files.getUploadURLExternal API
// 2. Send the file as a post to the URL provided by slack
// 3. Complete the upload and share it to the specified channel using files.completeUploadExternal
//
// Slack Docs: https://api.slack.com/messaging/files#uploading_files
func (api *Client) UploadFileV2Context(ctx context.Context, params UploadFileV2Parameters) (file *FileSummary, err error) {
if params.Filename == "" {
return nil, fmt.Errorf("file.upload.v2: filename cannot be empty")
@@ -556,9 +580,7 @@ func (api *Client) UploadFileV2Context(ctx context.Context, params UploadFileV2P
if params.FileSize == 0 {
return nil, fmt.Errorf("file.upload.v2: file size cannot be 0")
}
if params.Channel == "" {
return nil, fmt.Errorf("file.upload.v2: channel cannot be empty")
}
u, err := api.getUploadURLExternal(ctx, getUploadURLExternalParameters{
altText: params.AltTxt,
fileName: params.Filename,
+20 -10
View File
@@ -15,12 +15,14 @@ type Manifest struct {
OAuthConfig OAuthConfig `json:"oauth_config,omitempty" yaml:"oauth_config,omitempty"`
}
// CreateManifest creates an app from an app manifest
// CreateManifest creates an app from an app manifest.
// For more details, see CreateManifestContext documentation.
func (api *Client) CreateManifest(manifest *Manifest, token string) (*ManifestResponse, error) {
return api.CreateManifestContext(context.Background(), manifest, token)
}
// CreateManifestContext creates an app from an app manifest with a custom context
// CreateManifestContext creates an app from an app manifest with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.manifest.create
func (api *Client) CreateManifestContext(ctx context.Context, manifest *Manifest, token string) (*ManifestResponse, error) {
if token == "" {
token = api.configToken
@@ -45,12 +47,14 @@ func (api *Client) CreateManifestContext(ctx context.Context, manifest *Manifest
return response, response.Err()
}
// DeleteManifest permanently deletes an app created through app manifests
// DeleteManifest permanently deletes an app created through app manifests.
// For more details, see DeleteManifestContext documentation.
func (api *Client) DeleteManifest(token string, appId string) (*SlackResponse, error) {
return api.DeleteManifestContext(context.Background(), token, appId)
}
// DeleteManifestContext permanently deletes an app created through app manifests with a custom context
// DeleteManifestContext permanently deletes an app created through app manifests with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.manifest.delete
func (api *Client) DeleteManifestContext(ctx context.Context, token string, appId string) (*SlackResponse, error) {
if token == "" {
token = api.configToken
@@ -70,12 +74,14 @@ func (api *Client) DeleteManifestContext(ctx context.Context, token string, appI
return response, response.Err()
}
// ExportManifest exports an app manifest from an existing app
// ExportManifest exports an app manifest from an existing app.
// For more details, see ExportManifestContext documentation.
func (api *Client) ExportManifest(token string, appId string) (*Manifest, error) {
return api.ExportManifestContext(context.Background(), token, appId)
}
// ExportManifestContext exports an app manifest from an existing app with a custom context
// ExportManifestContext exports an app manifest from an existing app with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.manifest.export
func (api *Client) ExportManifestContext(ctx context.Context, token string, appId string) (*Manifest, error) {
if token == "" {
token = api.configToken
@@ -95,12 +101,14 @@ func (api *Client) ExportManifestContext(ctx context.Context, token string, appI
return &response.Manifest, response.Err()
}
// UpdateManifest updates an app from an app manifest
// UpdateManifest updates an app from an app manifest.
// For more details, see UpdateManifestContext documentation.
func (api *Client) UpdateManifest(manifest *Manifest, token string, appId string) (*UpdateManifestResponse, error) {
return api.UpdateManifestContext(context.Background(), manifest, token, appId)
}
// UpdateManifestContext updates an app from an app manifest with a custom context
// UpdateManifestContext updates an app from an app manifest with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.manifest.update
func (api *Client) UpdateManifestContext(ctx context.Context, manifest *Manifest, token string, appId string) (*UpdateManifestResponse, error) {
if token == "" {
token = api.configToken
@@ -126,12 +134,14 @@ func (api *Client) UpdateManifestContext(ctx context.Context, manifest *Manifest
return response, response.Err()
}
// ValidateManifest sends a request to apps.manifest.validate to validate your app manifest
// ValidateManifest sends a request to apps.manifest.validate to validate your app manifest.
// For more details, see ValidateManifestContext documentation.
func (api *Client) ValidateManifest(manifest *Manifest, token string, appId string) (*ManifestResponse, error) {
return api.ValidateManifestContext(context.Background(), manifest, token, appId)
}
// ValidateManifestContext sends a request to apps.manifest.validate to validate your app manifest with a custom context
// ValidateManifestContext sends a request to apps.manifest.validate to validate your app manifest with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.manifest.validate
func (api *Client) ValidateManifestContext(ctx context.Context, manifest *Manifest, token string, appId string) (*ManifestResponse, error) {
if token == "" {
token = api.configToken
+26 -6
View File
@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/http"
@@ -63,13 +62,12 @@ func (e *RateLimitedError) Retryable() bool {
return true
}
func fileUploadReq(ctx context.Context, path string, values url.Values, r io.Reader) (*http.Request, error) {
func fileUploadReq(ctx context.Context, path string, r io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, path, r)
if err != nil {
return nil, err
}
req.URL.RawQuery = values.Encode()
return req, nil
}
@@ -127,7 +125,7 @@ func jsonReq(ctx context.Context, endpoint string, body interface{}) (req *http.
}
func parseResponseBody(body io.ReadCloser, intf interface{}, d Debug) error {
response, err := ioutil.ReadAll(body)
response, err := io.ReadAll(body)
if err != nil {
return err
}
@@ -156,9 +154,16 @@ func postLocalWithMultipartResponse(ctx context.Context, client httpClient, meth
func postWithMultipartResponse(ctx context.Context, client httpClient, path, name, fieldname, token string, values url.Values, r io.Reader, intf interface{}, d Debug) error {
pipeReader, pipeWriter := io.Pipe()
wr := multipart.NewWriter(pipeWriter)
errc := make(chan error)
go func() {
defer pipeWriter.Close()
defer wr.Close()
err := createFormFields(wr, values)
if err != nil {
errc <- err
return
}
ioWriter, err := wr.CreateFormFile(fieldname, name)
if err != nil {
errc <- err
@@ -174,7 +179,8 @@ func postWithMultipartResponse(ctx context.Context, client httpClient, path, nam
return
}
}()
req, err := fileUploadReq(ctx, path, values, pipeReader)
req, err := fileUploadReq(ctx, path, pipeReader)
if err != nil {
return err
}
@@ -200,6 +206,20 @@ func postWithMultipartResponse(ctx context.Context, client httpClient, path, nam
}
}
func createFormFields(mw *multipart.Writer, values url.Values) error {
for key, value := range values {
writer, err := mw.CreateFormField(key)
if err != nil {
return err
}
_, err = writer.Write([]byte(value[0]))
if err != nil {
return err
}
}
return nil
}
func doPost(ctx context.Context, client httpClient, req *http.Request, parser responseParser, d Debug) error {
resp, err := client.Do(req)
if err != nil {
@@ -316,7 +336,7 @@ func newJSONParser(dst interface{}) responseParser {
func newTextParser(dst interface{}) responseParser {
return func(resp *http.Response) error {
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
+21 -10
View File
@@ -79,12 +79,14 @@ type OpenIDConnectResponse struct {
SlackResponse
}
// GetOAuthToken retrieves an AccessToken
// GetOAuthToken retrieves an AccessToken.
// For more details, see GetOAuthTokenContext documentation.
func GetOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
return GetOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOAuthTokenContext retrieves an AccessToken with a custom context
// GetOAuthTokenContext retrieves an AccessToken with a custom context.
// For more details, see GetOAuthResponseContext documentation.
func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
if err != nil {
@@ -94,11 +96,13 @@ func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clie
}
// GetBotOAuthToken retrieves top-level and bot AccessToken - https://api.slack.com/legacy/oauth#bot_user_access_tokens
// For more details, see GetBotOAuthTokenContext documentation.
func GetBotOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) {
return GetBotOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetBotOAuthTokenContext retrieves top-level and bot AccessToken with a custom context
// GetBotOAuthTokenContext retrieves top-level and bot AccessToken with a custom context.
// For more details, see GetOAuthResponseContext documentation.
func GetBotOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) {
response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
if err != nil {
@@ -107,12 +111,14 @@ func GetBotOAuthTokenContext(ctx context.Context, client httpClient, clientID, c
return response.AccessToken, response.Scope, response.Bot, nil
}
// GetOAuthResponse retrieves OAuth response
// GetOAuthResponse retrieves OAuth response.
// For more details, see GetOAuthResponseContext documentation.
func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOAuthResponseContext retrieves OAuth response with custom context
// GetOAuthResponseContext retrieves OAuth response with custom context.
// Slack API docs: https://api.slack.com/methods/oauth.access
func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
values := url.Values{
"client_id": {clientID},
@@ -127,12 +133,14 @@ func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, c
return response, response.Err()
}
// GetOAuthV2Response gets a V2 OAuth access token response - https://api.slack.com/methods/oauth.v2.access
// GetOAuthV2Response gets a V2 OAuth access token response.
// For more details, see GetOAuthV2ResponseContext documentation.
func GetOAuthV2Response(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthV2Response, err error) {
return GetOAuthV2ResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOAuthV2ResponseContext with a context, gets a V2 OAuth access token response
// GetOAuthV2ResponseContext with a context, gets a V2 OAuth access token response.
// Slack API docs: https://api.slack.com/methods/oauth.v2.access
func GetOAuthV2ResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthV2Response, err error) {
values := url.Values{
"client_id": {clientID},
@@ -147,12 +155,14 @@ func GetOAuthV2ResponseContext(ctx context.Context, client httpClient, clientID,
return response, response.Err()
}
// RefreshOAuthV2Token with a context, gets a V2 OAuth access token response
// RefreshOAuthV2Token with a context, gets a V2 OAuth access token response.
// For more details, see RefreshOAuthV2TokenContext documentation.
func RefreshOAuthV2Token(client httpClient, clientID, clientSecret, refreshToken string) (resp *OAuthV2Response, err error) {
return RefreshOAuthV2TokenContext(context.Background(), client, clientID, clientSecret, refreshToken)
}
// RefreshOAuthV2TokenContext with a context, gets a V2 OAuth access token response
// RefreshOAuthV2TokenContext with a context, gets a V2 OAuth access token response.
// Slack API docs: https://api.slack.com/methods/oauth.v2.access
func RefreshOAuthV2TokenContext(ctx context.Context, client httpClient, clientID, clientSecret, refreshToken string) (resp *OAuthV2Response, err error) {
values := url.Values{
"client_id": {clientID},
@@ -168,12 +178,13 @@ func RefreshOAuthV2TokenContext(ctx context.Context, client httpClient, clientID
}
// GetOpenIDConnectToken exchanges a temporary OAuth verifier code for an access token for Sign in with Slack.
// see: https://api.slack.com/methods/openid.connect.token
// For more details, see GetOpenIDConnectTokenContext documentation.
func GetOpenIDConnectToken(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OpenIDConnectResponse, err error) {
return GetOpenIDConnectTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOpenIDConnectTokenContext with a context, gets an access token for Sign in with Slack.
// Slack API docs: https://api.slack.com/methods/openid.connect.token
func GetOpenIDConnectTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OpenIDConnectResponse, err error) {
values := url.Values{
"client_id": {clientID},
+10 -4
View File
@@ -12,12 +12,14 @@ type listPinsResponseFull struct {
SlackResponse
}
// AddPin pins an item in a channel
// AddPin pins an item in a channel.
// For more details, see AddPinContext documentation.
func (api *Client) AddPin(channel string, item ItemRef) error {
return api.AddPinContext(context.Background(), channel, item)
}
// AddPinContext pins an item in a channel with a custom context
// AddPinContext pins an item in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/pins.add
func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemRef) error {
values := url.Values{
"channel": {channel},
@@ -41,12 +43,14 @@ func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemR
return response.Err()
}
// RemovePin un-pins an item from a channel
// RemovePin un-pins an item from a channel.
// For more details, see RemovePinContext documentation.
func (api *Client) RemovePin(channel string, item ItemRef) error {
return api.RemovePinContext(context.Background(), channel, item)
}
// RemovePinContext un-pins an item from a channel with a custom context
// RemovePinContext un-pins an item from a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/pins.remove
func (api *Client) RemovePinContext(ctx context.Context, channel string, item ItemRef) error {
values := url.Values{
"channel": {channel},
@@ -71,11 +75,13 @@ func (api *Client) RemovePinContext(ctx context.Context, channel string, item It
}
// ListPins returns information about the items a user reacted to.
// For more details, see ListPinsContext documentation.
func (api *Client) ListPins(channel string) ([]Item, *Paging, error) {
return api.ListPinsContext(context.Background(), channel)
}
// ListPinsContext returns information about the items a user reacted to with a custom context.
// Slack API docs: https://api.slack.com/methods/pins.list
func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item, *Paging, error) {
values := url.Values{
"channel": {channel},
+17 -5
View File
@@ -67,10 +67,11 @@ const (
// ListReactionsParameters is the inputs to find all reactions by a user.
type ListReactionsParameters struct {
User string
Count int
Page int
Full bool
User string
TeamID string
Count int
Page int
Full bool
}
// NewListReactionsParameters initializes the inputs to find all reactions
@@ -128,11 +129,13 @@ func (res listReactionsResponseFull) extractReactedItems() []ReactedItem {
}
// AddReaction adds a reaction emoji to a message, file or file comment.
// For more details, see AddReactionContext documentation.
func (api *Client) AddReaction(name string, item ItemRef) error {
return api.AddReactionContext(context.Background(), name, item)
}
// AddReactionContext adds a reaction emoji to a message, file or file comment with a custom context.
// Slack API docs: https://api.slack.com/methods/reactions.add
func (api *Client) AddReactionContext(ctx context.Context, name string, item ItemRef) error {
values := url.Values{
"token": {api.token},
@@ -162,11 +165,13 @@ func (api *Client) AddReactionContext(ctx context.Context, name string, item Ite
}
// RemoveReaction removes a reaction emoji from a message, file or file comment.
// For more details, see RemoveReactionContext documentation.
func (api *Client) RemoveReaction(name string, item ItemRef) error {
return api.RemoveReactionContext(context.Background(), name, item)
}
// RemoveReactionContext removes a reaction emoji from a message, file or file comment with a custom context.
// Slack API docs: https://api.slack.com/methods/reactions.remove
func (api *Client) RemoveReactionContext(ctx context.Context, name string, item ItemRef) error {
values := url.Values{
"token": {api.token},
@@ -196,11 +201,13 @@ func (api *Client) RemoveReactionContext(ctx context.Context, name string, item
}
// GetReactions returns details about the reactions on an item.
// For more details, see GetReactionsContext documentation.
func (api *Client) GetReactions(item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
return api.GetReactionsContext(context.Background(), item, params)
}
// GetReactionsContext returns details about the reactions on an item with a custom context
// GetReactionsContext returns details about the reactions on an item with a custom context.
// Slack API docs: https://api.slack.com/methods/reactions.get
func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
values := url.Values{
"token": {api.token},
@@ -234,11 +241,13 @@ func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params
}
// ListReactions returns information about the items a user reacted to.
// For more details, see ListReactionsContext documentation.
func (api *Client) ListReactions(params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
return api.ListReactionsContext(context.Background(), params)
}
// ListReactionsContext returns information about the items a user reacted to with a custom context.
// Slack API docs: https://api.slack.com/methods/reactions.list
func (api *Client) ListReactionsContext(ctx context.Context, params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
values := url.Values{
"token": {api.token},
@@ -246,6 +255,9 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
if params.User != DEFAULT_REACTIONS_USER {
values.Add("user", params.User)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Count != DEFAULT_REACTIONS_COUNT {
values.Add("count", strconv.Itoa(params.Count))
}
+11 -25
View File
@@ -41,23 +41,18 @@ func (api *Client) doReminders(ctx context.Context, path string, values url.Valu
// create an array of pointers to reminders
var reminders = make([]*Reminder, 0, len(response.Reminders))
for _, reminder := range response.Reminders {
reminders = append(reminders, reminder)
}
reminders = append(reminders, response.Reminders...)
return reminders, response.Err()
}
// ListReminders lists all the reminders created by or for the authenticated user
//
// See https://api.slack.com/methods/reminders.list
// For more details, see ListRemindersContext documentation.
func (api *Client) ListReminders() ([]*Reminder, error) {
return api.ListRemindersContext(context.Background())
}
// ListRemindersContext lists all the reminders created by or for the authenticated user with a custom context
//
// For more details, see ListReminders documentation.
// ListRemindersContext lists all the reminders created by or for the authenticated user with a custom context.
// Slack API docs: https://api.slack.com/methods/reminders.list
func (api *Client) ListRemindersContext(ctx context.Context) ([]*Reminder, error) {
values := url.Values{
"token": {api.token},
@@ -66,17 +61,14 @@ func (api *Client) ListRemindersContext(ctx context.Context) ([]*Reminder, error
}
// AddChannelReminder adds a reminder for a channel.
//
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
// reminders on a channel is currently undocumented but has been tested to
// work)
// For more details, see AddChannelReminderContext documentation.
func (api *Client) AddChannelReminder(channelID, text, time string) (*Reminder, error) {
return api.AddChannelReminderContext(context.Background(), channelID, text, time)
}
// AddChannelReminderContext adds a reminder for a channel with a custom context
//
// For more details, see AddChannelReminder documentation.
// NOTE: the ability to set reminders on a channel is currently undocumented but has been tested to work.
// Slack API docs: https://api.slack.com/methods/reminders.add
func (api *Client) AddChannelReminderContext(ctx context.Context, channelID, text, time string) (*Reminder, error) {
values := url.Values{
"token": {api.token},
@@ -88,17 +80,13 @@ func (api *Client) AddChannelReminderContext(ctx context.Context, channelID, tex
}
// AddUserReminder adds a reminder for a user.
//
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
// reminders on a channel is currently undocumented but has been tested to
// work)
// For more details, see AddUserReminderContext documentation.
func (api *Client) AddUserReminder(userID, text, time string) (*Reminder, error) {
return api.AddUserReminderContext(context.Background(), userID, text, time)
}
// AddUserReminderContext adds a reminder for a user with a custom context
//
// For more details, see AddUserReminder documentation.
// Slack API docs: https://api.slack.com/methods/reminders.add
func (api *Client) AddUserReminderContext(ctx context.Context, userID, text, time string) (*Reminder, error) {
values := url.Values{
"token": {api.token},
@@ -110,15 +98,13 @@ func (api *Client) AddUserReminderContext(ctx context.Context, userID, text, tim
}
// DeleteReminder deletes an existing reminder.
//
// See https://api.slack.com/methods/reminders.delete
// For more details, see DeleteReminderContext documentation.
func (api *Client) DeleteReminder(id string) error {
return api.DeleteReminderContext(context.Background(), id)
}
// DeleteReminderContext deletes an existing reminder with a custom context
//
// For more details, see DeleteReminder documentation.
// Slack API docs: https://api.slack.com/methods/reminders.delete
func (api *Client) DeleteReminderContext(ctx context.Context, id string) error {
values := url.Values{
"token": {api.token},
+17 -24
View File
@@ -97,14 +97,13 @@ func (api *Client) remoteFileRequest(ctx context.Context, path string, values ur
}
// AddRemoteFile adds a remote file. Unlike regular files, remote files must be explicitly shared.
// For more details:
// https://api.slack.com/methods/files.remote.add
// For more details see the AddRemoteFileContext documentation.
func (api *Client) AddRemoteFile(params RemoteFileParameters) (*RemoteFile, error) {
return api.AddRemoteFileContext(context.Background(), params)
}
// AddRemoteFileContext adds a remote file and setting a custom context
// For more details see the AddRemoteFile documentation.
// Slack API docs: https://api.slack.com/methods/files.remote.add
func (api *Client) AddRemoteFileContext(ctx context.Context, params RemoteFileParameters) (remotefile *RemoteFile, err error) {
if params.ExternalID == "" || params.ExternalURL == "" || params.Title == "" {
return nil, ErrParametersMissing
@@ -138,14 +137,13 @@ func (api *Client) AddRemoteFileContext(ctx context.Context, params RemoteFilePa
}
// ListRemoteFiles retrieves all remote files according to the parameters given. Uses cursor based pagination.
// For more details:
// https://api.slack.com/methods/files.remote.list
// For more details see the ListRemoteFilesContext documentation.
func (api *Client) ListRemoteFiles(params ListRemoteFilesParameters) ([]RemoteFile, error) {
return api.ListRemoteFilesContext(context.Background(), params)
}
// ListRemoteFilesContext retrieves all remote files according to the parameters given with a custom context. Uses cursor based pagination.
// For more details see the ListRemoteFiles documentation.
// Slack API docs: https://api.slack.com/methods/files.remote.list
func (api *Client) ListRemoteFilesContext(ctx context.Context, params ListRemoteFilesParameters) ([]RemoteFile, error) {
values := url.Values{
"token": {api.token},
@@ -177,14 +175,13 @@ func (api *Client) ListRemoteFilesContext(ctx context.Context, params ListRemote
}
// GetRemoteFileInfo retrieves the complete remote file information.
// For more details:
// https://api.slack.com/methods/files.remote.info
// For more details see the GetRemoteFileInfoContext documentation.
func (api *Client) GetRemoteFileInfo(externalID, fileID string) (remotefile *RemoteFile, err error) {
return api.GetRemoteFileInfoContext(context.Background(), externalID, fileID)
}
// GetRemoteFileInfoContext retrieves the complete remote file information given with a custom context.
// For more details see the GetRemoteFileInfo documentation.
// Slack API docs: https://api.slack.com/methods/files.remote.info
func (api *Client) GetRemoteFileInfoContext(ctx context.Context, externalID, fileID string) (remotefile *RemoteFile, err error) {
if fileID == "" && externalID == "" {
return nil, fmt.Errorf("either externalID or fileID is required")
@@ -208,15 +205,14 @@ func (api *Client) GetRemoteFileInfoContext(ctx context.Context, externalID, fil
return &response.RemoteFile, err
}
// ShareRemoteFile shares a remote file to channels
// For more details:
// https://api.slack.com/methods/files.remote.share
// ShareRemoteFile shares a remote file to channels.
// For more details see the ShareRemoteFileContext documentation.
func (api *Client) ShareRemoteFile(channels []string, externalID, fileID string) (file *RemoteFile, err error) {
return api.ShareRemoteFileContext(context.Background(), channels, externalID, fileID)
}
// ShareRemoteFileContext shares a remote file to channels with a custom context.
// For more details see the ShareRemoteFile documentation.
// Slack API docs: https://api.slack.com/methods/files.remote.share
func (api *Client) ShareRemoteFileContext(ctx context.Context, channels []string, externalID, fileID string) (file *RemoteFile, err error) {
if channels == nil || len(channels) == 0 {
return nil, ErrParametersMissing
@@ -241,20 +237,17 @@ func (api *Client) ShareRemoteFileContext(ctx context.Context, channels []string
return &response.RemoteFile, err
}
// UpdateRemoteFile updates a remote file
// For more details:
// https://api.slack.com/methods/files.remote.update
// UpdateRemoteFile updates a remote file.
// For more details see the UpdateRemoteFileContext documentation.
func (api *Client) UpdateRemoteFile(fileID string, params RemoteFileParameters) (remotefile *RemoteFile, err error) {
return api.UpdateRemoteFileContext(context.Background(), fileID, params)
}
// UpdateRemoteFileContext updates a remote file with a custom context
// For more details see the UpdateRemoteFile documentation.
// UpdateRemoteFileContext updates a remote file with a custom context.
// Slack API docs: https://api.slack.com/methods/files.remote.update
func (api *Client) UpdateRemoteFileContext(ctx context.Context, fileID string, params RemoteFileParameters) (remotefile *RemoteFile, err error) {
response := &remoteFileResponseFull{}
values := url.Values{
"token": {api.token},
}
values := url.Values{}
if fileID != "" {
values.Add("file", fileID)
}
@@ -276,6 +269,7 @@ func (api *Client) UpdateRemoteFileContext(ctx context.Context, fileID string, p
if params.PreviewImageReader != nil {
err = postWithMultipartResponse(ctx, api.httpclient, api.endpoint+"files.remote.update", "preview.png", "preview_image", api.token, values, params.PreviewImageReader, response, api)
} else {
values.Add("token", api.token)
response, err = api.remoteFileRequest(ctx, "files.remote.update", values)
}
@@ -287,14 +281,13 @@ func (api *Client) UpdateRemoteFileContext(ctx context.Context, fileID string, p
}
// RemoveRemoteFile removes a remote file.
// For more details:
// https://api.slack.com/methods/files.remote.remove
// For more information see the RemoveRemoteFileContext documentation.
func (api *Client) RemoveRemoteFile(externalID, fileID string) (err error) {
return api.RemoveRemoteFileContext(context.Background(), externalID, fileID)
}
// RemoveRemoteFileContext removes a remote file with a custom context
// For more information see the RemoveRemoteFiles documentation.
// Slack API docs: https://api.slack.com/methods/files.remote.remove
func (api *Client) RemoveRemoteFileContext(ctx context.Context, externalID, fileID string) (err error) {
if fileID == "" && externalID == "" {
return fmt.Errorf("either externalID or fileID is required")
+4
View File
@@ -15,6 +15,7 @@ const (
)
type SearchParameters struct {
TeamID string
Sort string
SortDirection string
Highlight bool
@@ -93,6 +94,9 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
"token": {api.token},
"query": {query},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Sort != DEFAULT_SEARCH_SORT {
values.Add("sort", params.Sort)
}
+12 -7
View File
@@ -36,12 +36,14 @@ func NewStarsParameters() StarsParameters {
}
}
// AddStar stars an item in a channel
// AddStar stars an item in a channel.
// For more information see the AddStarContext documentation.
func (api *Client) AddStar(channel string, item ItemRef) error {
return api.AddStarContext(context.Background(), channel, item)
}
// AddStarContext stars an item in a channel with a custom context
// AddStarContext stars an item in a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/stars.add
func (api *Client) AddStarContext(ctx context.Context, channel string, item ItemRef) error {
values := url.Values{
"channel": {channel},
@@ -65,12 +67,14 @@ func (api *Client) AddStarContext(ctx context.Context, channel string, item Item
return response.Err()
}
// RemoveStar removes a starred item from a channel
// RemoveStar removes a starred item from a channel.
// For more information see the RemoveStarContext documentation.
func (api *Client) RemoveStar(channel string, item ItemRef) error {
return api.RemoveStarContext(context.Background(), channel, item)
}
// RemoveStarContext removes a starred item from a channel with a custom context
// RemoveStarContext removes a starred item from a channel with a custom context.
// Slack API docs: https://api.slack.com/methods/stars.remove
func (api *Client) RemoveStarContext(ctx context.Context, channel string, item ItemRef) error {
values := url.Values{
"channel": {channel},
@@ -94,12 +98,14 @@ func (api *Client) RemoveStarContext(ctx context.Context, channel string, item I
return response.Err()
}
// ListStars returns information about the stars a user added
// ListStars returns information about the stars a user added.
// For more information see the ListStarsContext documentation.
func (api *Client) ListStars(params StarsParameters) ([]Item, *Paging, error) {
return api.ListStarsContext(context.Background(), params)
}
// ListStarsContext returns information about the stars a user added with a custom context
// ListStarsContext returns information about the stars a user added with a custom context.
// Slack API docs: https://api.slack.com/methods/stars.list
func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters) ([]Item, *Paging, error) {
values := url.Values{
"token": {api.token},
@@ -147,7 +153,6 @@ func (api *Client) GetStarred(params StarsParameters) ([]StarredItem, *Paging, e
}
// GetStarredContext returns a list of StarredItem items with a custom context
//
// For more details see GetStarred
func (api *Client) GetStarredContext(ctx context.Context, params StarsParameters) ([]StarredItem, *Paging, error) {
items, paging, err := api.ListStarsContext(ctx, params)
+46 -32
View File
@@ -74,8 +74,9 @@ type BillingActive struct {
// AccessLogParameters contains all the parameters necessary (including the optional ones) for a GetAccessLogs() request
type AccessLogParameters struct {
Count int
Page int
TeamID string
Count int
Page int
}
// NewAccessLogParameters provides an instance of AccessLogParameters with all the sane default values set
@@ -124,12 +125,14 @@ func (api *Client) teamProfileRequest(ctx context.Context, client httpClient, pa
return response, response.Err()
}
// GetTeamInfo gets the Team Information of the user
// GetTeamInfo gets the Team Information of the user.
// For more information see the GetTeamInfoContext documentation.
func (api *Client) GetTeamInfo() (*TeamInfo, error) {
return api.GetTeamInfoContext(context.Background())
}
// GetOtherTeamInfoContext gets Team information for any team with a custom context
// GetOtherTeamInfoContext gets Team information for any team with a custom context.
// Slack API docs: https://api.slack.com/methods/team.info
func (api *Client) GetOtherTeamInfoContext(ctx context.Context, team string) (*TeamInfo, error) {
if team == "" {
return api.GetTeamInfoContext(ctx)
@@ -145,12 +148,14 @@ func (api *Client) GetOtherTeamInfoContext(ctx context.Context, team string) (*T
return &response.Team, nil
}
// GetOtherTeamInfo gets Team information for any team
// GetOtherTeamInfo gets Team information for any team.
// For more information see the GetOtherTeamInfoContext documentation.
func (api *Client) GetOtherTeamInfo(team string) (*TeamInfo, error) {
return api.GetOtherTeamInfoContext(context.Background(), team)
}
// GetTeamInfoContext gets the Team Information of the user with a custom context
// GetTeamInfoContext gets the Team Information of the user with a custom context.
// Slack API docs: https://api.slack.com/methods/team.info
func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
values := url.Values{
"token": {api.token},
@@ -163,35 +168,44 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
return &response.Team, nil
}
// GetTeamProfile gets the Team Profile settings of the user
func (api *Client) GetTeamProfile() (*TeamProfile, error) {
return api.GetTeamProfileContext(context.Background())
// GetTeamProfile gets the Team Profile settings of the user.
// For more information see the GetTeamProfileContext documentation.
func (api *Client) GetTeamProfile(teamID ...string) (*TeamProfile, error) {
return api.GetTeamProfileContext(context.Background(), teamID...)
}
// GetTeamProfileContext gets the Team Profile settings of the user with a custom context
func (api *Client) GetTeamProfileContext(ctx context.Context) (*TeamProfile, error) {
// GetTeamProfileContext gets the Team Profile settings of the user with a custom context.
// Slack API docs: https://api.slack.com/methods/team.profile.get
func (api *Client) GetTeamProfileContext(ctx context.Context, teamID ...string) (*TeamProfile, error) {
values := url.Values{
"token": {api.token},
}
if len(teamID) > 0 {
values["team_id"] = teamID
}
response, err := api.teamProfileRequest(ctx, api.httpclient, "team.profile.get", values)
if err != nil {
return nil, err
}
return &response.Profile, nil
}
// GetAccessLogs retrieves a page of logins according to the parameters given
// GetAccessLogs retrieves a page of logins according to the parameters given.
// For more information see the GetAccessLogsContext documentation.
func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging, error) {
return api.GetAccessLogsContext(context.Background(), params)
}
// GetAccessLogsContext retrieves a page of logins according to the parameters given with a custom context
// GetAccessLogsContext retrieves a page of logins according to the parameters given with a custom context.
// Slack API docs: https://api.slack.com/methods/team.accessLogs
func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogParameters) ([]Login, *Paging, error) {
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Count != DEFAULT_LOGINS_COUNT {
values.Add("count", strconv.Itoa(params.Count))
}
@@ -206,31 +220,31 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
return response.Logins, &response.Paging, nil
}
// GetBillableInfo ...
func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error) {
return api.GetBillableInfoContext(context.Background(), user)
type GetBillableInfoParams struct {
User string
TeamID string
}
// GetBillableInfoContext ...
func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map[string]BillingActive, error) {
values := url.Values{
"token": {api.token},
"user": {user},
}
return api.billableInfoRequest(ctx, "team.billableInfo", values)
// GetBillableInfo gets the billable users information of the team.
// For more information see the GetBillableInfoContext documentation.
func (api *Client) GetBillableInfo(params GetBillableInfoParams) (map[string]BillingActive, error) {
return api.GetBillableInfoContext(context.Background(), params)
}
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
func (api *Client) GetBillableInfoForTeam() (map[string]BillingActive, error) {
return api.GetBillableInfoForTeamContext(context.Background())
}
// GetBillableInfoForTeamContext returns the billing_active status of all users on the team with a custom context
func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[string]BillingActive, error) {
// GetBillableInfoContext gets the billable users information of the team with a custom context.
// Slack API docs: https://api.slack.com/methods/team.billableInfo
func (api *Client) GetBillableInfoContext(ctx context.Context, params GetBillableInfoParams) (map[string]BillingActive, error) {
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.User != "" {
values.Add("user", params.User)
}
return api.billableInfoRequest(ctx, "team.billableInfo", values)
}
+4 -2
View File
@@ -5,12 +5,14 @@ import (
"net/url"
)
// RotateTokens exchanges a refresh token for a new app configuration token
// RotateTokens exchanges a refresh token for a new app configuration token.
// For more information see the RotateTokensContext documentation.
func (api *Client) RotateTokens(configToken string, refreshToken string) (*TokenResponse, error) {
return api.RotateTokensContext(context.Background(), configToken, refreshToken)
}
// RotateTokensContext exchanges a refresh token for a new app configuration token with a custom context
// RotateTokensContext exchanges a refresh token for a new app configuration token with a custom context.
// Slack API docs: https://api.slack.com/methods/tooling.tokens.rotate
func (api *Client) RotateTokensContext(ctx context.Context, configToken string, refreshToken string) (*TokenResponse, error) {
if configToken == "" {
configToken = api.configToken
+42 -14
View File
@@ -50,18 +50,24 @@ func (api *Client) userGroupRequest(ctx context.Context, path string, values url
return response, response.Err()
}
// CreateUserGroup creates a new user group
// CreateUserGroup creates a new user group.
// For more information see the CreateUserGroupContext documentation.
func (api *Client) CreateUserGroup(userGroup UserGroup) (UserGroup, error) {
return api.CreateUserGroupContext(context.Background(), userGroup)
}
// CreateUserGroupContext creates a new user group with a custom context
// CreateUserGroupContext creates a new user group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.create
func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGroup) (UserGroup, error) {
values := url.Values{
"token": {api.token},
"name": {userGroup.Name},
}
if userGroup.TeamID != "" {
values["team_id"] = []string{userGroup.TeamID}
}
if userGroup.Handle != "" {
values["handle"] = []string{userGroup.Handle}
}
@@ -81,12 +87,14 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
return response.UserGroup, nil
}
// DisableUserGroup disables an existing user group
// DisableUserGroup disables an existing user group.
// For more information see the DisableUserGroupContext documentation.
func (api *Client) DisableUserGroup(userGroup string) (UserGroup, error) {
return api.DisableUserGroupContext(context.Background(), userGroup)
}
// DisableUserGroupContext disables an existing user group with a custom context
// DisableUserGroupContext disables an existing user group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.disable
func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
values := url.Values{
"token": {api.token},
@@ -100,12 +108,14 @@ func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string
return response.UserGroup, nil
}
// EnableUserGroup enables an existing user group
// EnableUserGroup enables an existing user group.
// For more information see the EnableUserGroupContext documentation.
func (api *Client) EnableUserGroup(userGroup string) (UserGroup, error) {
return api.EnableUserGroupContext(context.Background(), userGroup)
}
// EnableUserGroupContext enables an existing user group with a custom context
// EnableUserGroupContext enables an existing user group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.enable
func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
values := url.Values{
"token": {api.token},
@@ -122,6 +132,12 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string)
// GetUserGroupsOption options for the GetUserGroups method call.
type GetUserGroupsOption func(*GetUserGroupsParams)
func GetUserGroupsOptionWithTeamID(teamID string) GetUserGroupsOption {
return func(params *GetUserGroupsParams) {
params.TeamID = teamID
}
}
// GetUserGroupsOptionIncludeCount include the number of users in each User Group (default: false)
func GetUserGroupsOptionIncludeCount(b bool) GetUserGroupsOption {
return func(params *GetUserGroupsParams) {
@@ -145,17 +161,20 @@ func GetUserGroupsOptionIncludeUsers(b bool) GetUserGroupsOption {
// GetUserGroupsParams contains arguments for GetUserGroups method call
type GetUserGroupsParams struct {
TeamID string
IncludeCount bool
IncludeDisabled bool
IncludeUsers bool
}
// GetUserGroups returns a list of user groups for the team
// GetUserGroups returns a list of user groups for the team.
// For more information see the GetUserGroupsContext documentation.
func (api *Client) GetUserGroups(options ...GetUserGroupsOption) ([]UserGroup, error) {
return api.GetUserGroupsContext(context.Background(), options...)
}
// GetUserGroupsContext returns a list of user groups for the team with a custom context
// GetUserGroupsContext returns a list of user groups for the team with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.list
func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserGroupsOption) ([]UserGroup, error) {
params := GetUserGroupsParams{}
@@ -166,6 +185,9 @@ func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserG
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.IncludeCount {
values.Add("include_count", "true")
}
@@ -222,12 +244,14 @@ type UpdateUserGroupsParams struct {
Channels *[]string
}
// UpdateUserGroup will update an existing user group
// UpdateUserGroup will update an existing user group.
// For more information see the UpdateUserGroupContext documentation.
func (api *Client) UpdateUserGroup(userGroupID string, options ...UpdateUserGroupsOption) (UserGroup, error) {
return api.UpdateUserGroupContext(context.Background(), userGroupID, options...)
}
// UpdateUserGroupContext will update an existing user group with a custom context
// UpdateUserGroupContext will update an existing user group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.update
func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroupID string, options ...UpdateUserGroupsOption) (UserGroup, error) {
params := UpdateUserGroupsParams{}
@@ -263,12 +287,14 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroupID strin
return response.UserGroup, nil
}
// GetUserGroupMembers will retrieve the current list of users in a group
// GetUserGroupMembers will retrieve the current list of users in a group.
// For more information see the GetUserGroupMembersContext documentation.
func (api *Client) GetUserGroupMembers(userGroup string) ([]string, error) {
return api.GetUserGroupMembersContext(context.Background(), userGroup)
}
// GetUserGroupMembersContext will retrieve the current list of users in a group with a custom context
// GetUserGroupMembersContext will retrieve the current list of users in a group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.users.list
func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup string) ([]string, error) {
values := url.Values{
"token": {api.token},
@@ -282,12 +308,14 @@ func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup str
return response.Users, nil
}
// UpdateUserGroupMembers will update the members of an existing user group
// UpdateUserGroupMembers will update the members of an existing user group.
// For more information see the UpdateUserGroupMembersContext documentation.
func (api *Client) UpdateUserGroupMembers(userGroup string, members string) (UserGroup, error) {
return api.UpdateUserGroupMembersContext(context.Background(), userGroup, members)
}
// UpdateUserGroupMembersContext will update the members of an existing user group with a custom context
// UpdateUserGroupMembersContext will update the members of an existing user group with a custom context.
// Slack API docs: https://api.slack.com/methods/usergroups.update
func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup string, members string) (UserGroup, error) {
values := url.Values{
"token": {api.token},
+60 -40
View File
@@ -227,11 +227,13 @@ func (api *Client) userRequest(ctx context.Context, path string, values url.Valu
}
// GetUserPresence will retrieve the current presence status of given user.
// For more information see the GetUserPresenceContext documentation.
func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
return api.GetUserPresenceContext(context.Background(), user)
}
// GetUserPresenceContext will retrieve the current presence status of given user with a custom context.
// Slack API docs: https://api.slack.com/methods/users.getPresence
func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*UserPresence, error) {
values := url.Values{
"token": {api.token},
@@ -245,12 +247,14 @@ func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*Us
return &response.UserPresence, nil
}
// GetUserInfo will retrieve the complete user information
// GetUserInfo will retrieve the complete user information.
// For more information see the GetUserInfoContext documentation.
func (api *Client) GetUserInfo(user string) (*User, error) {
return api.GetUserInfoContext(context.Background(), user)
}
// GetUserInfoContext will retrieve the complete user information with a custom context
// GetUserInfoContext will retrieve the complete user information with a custom context.
// Slack API docs: https://api.slack.com/methods/users.info
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
values := url.Values{
"token": {api.token},
@@ -265,12 +269,14 @@ func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User,
return &response.User, nil
}
// GetUsersInfo will retrieve the complete multi-users information
// GetUsersInfo will retrieve the complete multi-users information.
// For more information see the GetUsersInfoContext documentation.
func (api *Client) GetUsersInfo(users ...string) (*[]User, error) {
return api.GetUsersInfoContext(context.Background(), users...)
}
// GetUsersInfoContext will retrieve the complete multi-users information with a custom context
// GetUsersInfoContext will retrieve the complete multi-users information with a custom context.
// Slack API docs: https://api.slack.com/methods/users.info
func (api *Client) GetUsersInfoContext(ctx context.Context, users ...string) (*[]User, error) {
values := url.Values{
"token": {api.token},
@@ -407,12 +413,14 @@ func (api *Client) GetUsersContext(ctx context.Context, options ...GetUsersOptio
return results, p.Failure(err)
}
// GetUserByEmail will retrieve the complete user information by email
// GetUserByEmail will retrieve the complete user information by email.
// For more information see the GetUserByEmailContext documentation.
func (api *Client) GetUserByEmail(email string) (*User, error) {
return api.GetUserByEmailContext(context.Background(), email)
}
// GetUserByEmailContext will retrieve the complete user information by email with a custom context
// GetUserByEmailContext will retrieve the complete user information by email with a custom context.
// Slack API docs: https://api.slack.com/methods/users.lookupByEmail
func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
values := url.Values{
"token": {api.token},
@@ -425,12 +433,14 @@ func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*Us
return &response.User, nil
}
// SetUserAsActive marks the currently authenticated user as active
// SetUserAsActive marks the currently authenticated user as active.
// For more information see the SetUserAsActiveContext documentation.
func (api *Client) SetUserAsActive() error {
return api.SetUserAsActiveContext(context.Background())
}
// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
// SetUserAsActiveContext marks the currently authenticated user as active with a custom context.
// Slack API docs: https://api.slack.com/methods/users.setActive
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
values := url.Values{
"token": {api.token},
@@ -440,12 +450,14 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
return err
}
// SetUserPresence changes the currently authenticated user presence
// SetUserPresence changes the currently authenticated user presence.
// For more information see the SetUserPresenceContext documentation.
func (api *Client) SetUserPresence(presence string) error {
return api.SetUserPresenceContext(context.Background(), presence)
}
// SetUserPresenceContext changes the currently authenticated user presence with a custom context
// SetUserPresenceContext changes the currently authenticated user presence with a custom context.
// Slack API docs: https://api.slack.com/methods/users.setPresence
func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) error {
values := url.Values{
"token": {api.token},
@@ -456,12 +468,14 @@ func (api *Client) SetUserPresenceContext(ctx context.Context, presence string)
return err
}
// GetUserIdentity will retrieve user info available per identity scopes
// GetUserIdentity will retrieve user info available per identity scopes.
// For more information see the GetUserIdentityContext documentation.
func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
return api.GetUserIdentityContext(context.Background())
}
// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context.
// Slack API docs: https://api.slack.com/methods/users.identity
func (api *Client) GetUserIdentityContext(ctx context.Context) (response *UserIdentityResponse, err error) {
values := url.Values{
"token": {api.token},
@@ -480,12 +494,14 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (response *UserId
return response, nil
}
// SetUserPhoto changes the currently authenticated user's profile image
// SetUserPhoto changes the currently authenticated user's profile image.
// For more information see the SetUserPhotoContext documentation.
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
return api.SetUserPhotoContext(context.Background(), image, params)
}
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context.
// Slack API docs: https://api.slack.com/methods/users.setPhoto
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) (err error) {
response := &SlackResponse{}
values := url.Values{}
@@ -507,12 +523,14 @@ func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params
return response.Err()
}
// DeleteUserPhoto deletes the current authenticated user's profile image
// DeleteUserPhoto deletes the current authenticated user's profile image.
// For more information see the DeleteUserPhotoContext documentation.
func (api *Client) DeleteUserPhoto() error {
return api.DeleteUserPhotoContext(context.Background())
}
// DeleteUserPhotoContext deletes the current authenticated user's profile image with a custom context
// DeleteUserPhotoContext deletes the current authenticated user's profile image with a custom context.
// Slack API docs: https://api.slack.com/methods/users.deletePhoto
func (api *Client) DeleteUserPhotoContext(ctx context.Context) (err error) {
response := &SlackResponse{}
values := url.Values{
@@ -528,13 +546,13 @@ func (api *Client) DeleteUserPhotoContext(ctx context.Context) (err error) {
}
// SetUserRealName changes the currently authenticated user's realName
//
// For more information see SetUserRealNameContextWithUser
// For more information see the SetUserRealNameContextWithUser documentation.
func (api *Client) SetUserRealName(realName string) error {
return api.SetUserRealNameContextWithUser(context.Background(), "", realName)
}
// SetUserRealNameContextWithUser will set a real name for the provided user with a custom context
// SetUserRealNameContextWithUser will set a real name for the provided user with a custom context.
// Slack API docs: https://api.slack.com/methods/users.profile.set
func (api *Client) SetUserRealNameContextWithUser(ctx context.Context, user, realName string) error {
profile, err := json.Marshal(
&struct {
@@ -566,20 +584,22 @@ func (api *Client) SetUserRealNameContextWithUser(ctx context.Context, user, rea
return response.Err()
}
// SetUserCustomFields sets Custom Profile fields on the provided users account. Due to the non-repeating elements
// within the request, a map fields is required. The key in the map signifies the field that will be updated.
//
// Note: You may need to change the way the custom field is populated within the Profile section of the Admin Console from
// SCIM or User Entered to API.
//
// See GetTeamProfile for information to retrieve possible fields for your account.
// SetUserCustomFields sets Custom Profile fields on the provided users account.
// For more information see the SetUserCustomFieldsContext documentation.
func (api *Client) SetUserCustomFields(userID string, customFields map[string]UserProfileCustomField) error {
return api.SetUserCustomFieldsContext(context.Background(), userID, customFields)
}
// SetUserCustomFieldsContext will set a users custom profile field with context.
// SetUserCustomFieldsContext sets Custom Profile fields on the provided users account.
// Due to the non-repeating elements within the request, a map fields is required.
// The key in the map signifies the field that will be updated.
//
// For more information see SetUserCustomFields
// Note: You may need to change the way the custom field is populated within the Profile section of the Admin Console
// from SCIM or User Entered to API.
//
// See GetTeamProfile for information to retrieve possible fields for your account.
//
// Slack API docs: https://api.slack.com/methods/users.profile.set
func (api *Client) SetUserCustomFieldsContext(ctx context.Context, userID string, customFields map[string]UserProfileCustomField) error {
// Convert data to data type with custom marshall / unmarshall
@@ -615,32 +635,30 @@ func (api *Client) SetUserCustomFieldsContext(ctx context.Context, userID string
}
// SetUserCustomStatus will set a custom status and emoji for the currently
// authenticated user. If statusEmoji is "" and statusText is not, the Slack API
// will automatically set it to ":speech_balloon:". Otherwise, if both are ""
// the Slack API will unset the custom status/emoji. If statusExpiration is set to 0
// the status will not expire.
// SetUserCustomStatus will set a custom status and emoji for the currently authenticated user.
// For more information see the SetUserCustomStatusContext documentation.
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string, statusExpiration int64) error {
return api.SetUserCustomStatusContextWithUser(context.Background(), "", statusText, statusEmoji, statusExpiration)
}
// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context
//
// For more information see SetUserCustomStatus
// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context.
// For more information see the SetUserCustomStatusContextWithUser documentation.
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string, statusExpiration int64) error {
return api.SetUserCustomStatusContextWithUser(ctx, "", statusText, statusEmoji, statusExpiration)
}
// SetUserCustomStatusWithUser will set a custom status and emoji for the provided user.
//
// For more information see SetUserCustomStatus
// For more information see the SetUserCustomStatusContextWithUser documentation.
func (api *Client) SetUserCustomStatusWithUser(user, statusText, statusEmoji string, statusExpiration int64) error {
return api.SetUserCustomStatusContextWithUser(context.Background(), user, statusText, statusEmoji, statusExpiration)
}
// SetUserCustomStatusContextWithUser will set a custom status and emoji for the provided user with a custom context
// SetUserCustomStatusContextWithUser will set a custom status and emoji for the currently authenticated user.
// If statusEmoji is "" and statusText is not, the Slack API will automatically set it to ":speech_balloon:".
// Otherwise, if both are "" the Slack API will unset the custom status/emoji. If statusExpiration is set to 0
// the status will not expire.
//
// For more information see SetUserCustomStatus
// Slack API docs: https://api.slack.com/methods/users.profile.set
func (api *Client) SetUserCustomStatusContextWithUser(ctx context.Context, user, statusText, statusEmoji string, statusExpiration int64) error {
// XXX(theckman): this anonymous struct is for making requests to the Slack
// API for setting and unsetting a User's Custom Status/Emoji. To change
@@ -705,6 +723,7 @@ type GetUserProfileParameters struct {
}
// GetUserProfile retrieves a user's profile information.
// For more information see the GetUserProfileContext documentation.
func (api *Client) GetUserProfile(params *GetUserProfileParameters) (*UserProfile, error) {
return api.GetUserProfileContext(context.Background(), params)
}
@@ -715,6 +734,7 @@ type getUserProfileResponse struct {
}
// GetUserProfileContext retrieves a user's profile information with a context.
// Slack API docs: https://api.slack.com/methods/users.profile.get
func (api *Client) GetUserProfileContext(ctx context.Context, params *GetUserProfileParameters) (*UserProfile, error) {
values := url.Values{"token": {api.token}}
+9 -1
View File
@@ -155,6 +155,7 @@ type ViewResponse struct {
}
// OpenView opens a view for a user.
// For more information see the OpenViewContext documentation.
func (api *Client) OpenView(triggerID string, view ModalViewRequest) (*ViewResponse, error) {
return api.OpenViewContext(context.Background(), triggerID, view)
}
@@ -177,6 +178,7 @@ func ValidateUniqueBlockID(view ModalViewRequest) bool {
}
// OpenViewContext opens a view for a user with a custom context.
// Slack API docs: https://api.slack.com/methods/views.open
func (api *Client) OpenViewContext(
ctx context.Context,
triggerID string,
@@ -208,11 +210,13 @@ func (api *Client) OpenViewContext(
}
// PublishView publishes a static view for a user.
// For more information see the PublishViewContext documentation.
func (api *Client) PublishView(userID string, view HomeTabViewRequest, hash string) (*ViewResponse, error) {
return api.PublishViewContext(context.Background(), userID, view, hash)
}
// PublishViewContext publishes a static view for a user with a custom context.
// Slack API docs: https://api.slack.com/methods/views.publish
func (api *Client) PublishViewContext(
ctx context.Context,
userID string,
@@ -241,11 +245,13 @@ func (api *Client) PublishViewContext(
}
// PushView pushes a view onto the stack of a root view.
// For more information see the PushViewContext documentation.
func (api *Client) PushView(triggerID string, view ModalViewRequest) (*ViewResponse, error) {
return api.PushViewContext(context.Background(), triggerID, view)
}
// PublishViewContext pushes a view onto the stack of a root view with a custom context.
// PushViewContext pushes a view onto the stack of a root view with a custom context.
// Slack API docs: https://api.slack.com/methods/views.push
func (api *Client) PushViewContext(
ctx context.Context,
triggerID string,
@@ -272,11 +278,13 @@ func (api *Client) PushViewContext(
}
// UpdateView updates an existing view.
// For more information see the UpdateViewContext documentation.
func (api *Client) UpdateView(view ModalViewRequest, externalID, hash, viewID string) (*ViewResponse, error) {
return api.UpdateViewContext(context.Background(), view, externalID, hash, viewID)
}
// UpdateViewContext updates an existing view with a custom context.
// Slack API docs: https://api.slack.com/methods/views.update
func (api *Client) UpdateViewContext(
ctx context.Context,
view ModalViewRequest,
+3 -2
View File
@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
)
@@ -24,6 +23,8 @@ type WebhookMessage struct {
ReplaceOriginal bool `json:"replace_original"`
DeleteOriginal bool `json:"delete_original"`
ReplyBroadcast bool `json:"reply_broadcast,omitempty"`
UnfurlLinks bool `json:"unfurl_links,omitempty"`
UnfurlMedia bool `json:"unfurl_media,omitempty"`
}
func PostWebhook(url string, msg *WebhookMessage) error {
@@ -55,7 +56,7 @@ func PostWebhookCustomHTTPContext(ctx context.Context, url string, httpClient *h
return fmt.Errorf("failed to post webhook: %w", err)
}
defer func() {
io.Copy(ioutil.Discard, resp.Body)
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
+4 -1
View File
@@ -44,12 +44,15 @@ func NewConfigurationModalRequest(blocks Blocks, privateMetaData string, externa
}
}
// SaveWorkflowStepConfiguration opens a configuration modal for a workflow step.
// For more information see the SaveWorkflowStepConfigurationContext documentation.
func (api *Client) SaveWorkflowStepConfiguration(workflowStepEditID string, inputs *WorkflowStepInputs, outputs *[]WorkflowStepOutput) error {
return api.SaveWorkflowStepConfigurationContext(context.Background(), workflowStepEditID, inputs, outputs)
}
// SaveWorkflowStepConfigurationContext saves the configuration of a workflow step with a custom context.
// Slack API docs: https://api.slack.com/methods/workflows.updateStep
func (api *Client) SaveWorkflowStepConfigurationContext(ctx context.Context, workflowStepEditID string, inputs *WorkflowStepInputs, outputs *[]WorkflowStepOutput) error {
// More information: https://api.slack.com/methods/workflows.updateStep
wscr := WorkflowStepCompleteResponse{
WorkflowStepEditID: workflowStepEditID,
Inputs: inputs,
+1 -1
View File
@@ -1 +1 @@
1.21.9
1.22.6
+13
View File
@@ -41,6 +41,15 @@ coverage:
TEST_FREELIST_TYPE=array go test -v -timeout 30m \
-coverprofile cover-freelist-array.out -covermode atomic
BOLT_CMD=bbolt
build:
go build -o bin/${BOLT_CMD} ./cmd/${BOLT_CMD}
.PHONY: clean
clean: # Clean binaries
rm -f ./bin/${BOLT_CMD}
.PHONY: gofail-enable
gofail-enable: install-gofail
gofail enable .
@@ -61,3 +70,7 @@ test-failpoint:
@echo "[failpoint] array freelist test"
TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout 30m ./tests/failpoint
.PHONY: test-robustness # Running robustness tests requires root permission
test-robustness:
go test -v ${TESTFLAGS} ./tests/dmflakey -test.root
go test -v ${TESTFLAGS} ./tests/robustness -test.root
+5 -3
View File
@@ -524,7 +524,7 @@ func (db *DB) munmap() error {
// gofail: var unmapError string
// return errors.New(unmapError)
if err := munmap(db); err != nil {
return fmt.Errorf("unmap error: " + err.Error())
return fmt.Errorf("unmap error: %v", err.Error())
}
return nil
@@ -571,7 +571,7 @@ func (db *DB) munlock(fileSize int) error {
// gofail: var munlockError string
// return errors.New(munlockError)
if err := munlock(db, fileSize); err != nil {
return fmt.Errorf("munlock error: " + err.Error())
return fmt.Errorf("munlock error: %v", err.Error())
}
return nil
}
@@ -580,7 +580,7 @@ func (db *DB) mlock(fileSize int) error {
// gofail: var mlockError string
// return errors.New(mlockError)
if err := mlock(db, fileSize); err != nil {
return fmt.Errorf("mlock error: " + err.Error())
return fmt.Errorf("mlock error: %v", err.Error())
}
return nil
}
@@ -1159,6 +1159,8 @@ func (db *DB) grow(sz int) error {
// https://github.com/boltdb/bolt/issues/284
if !db.NoGrowSync && !db.readOnly {
if runtime.GOOS != "windows" {
// gofail: var resizeFileError string
// return errors.New(resizeFileError)
if err := db.file.Truncate(int64(sz)); err != nil {
return fmt.Errorf("file resize error: %s", err)
}
+8
View File
@@ -252,6 +252,14 @@ func (f *freelist) rollback(txid txid) {
}
// Remove pages from pending list and mark as free if allocated by txid.
delete(f.pending, txid)
// Remove pgids which are allocated by this txid
for pgid, tid := range f.allocs {
if tid == txid {
delete(f.allocs, pgid)
}
}
f.mergeSpans(m)
}
+7
View File
@@ -1,6 +1,7 @@
package bbolt
import (
"errors"
"fmt"
"io"
"os"
@@ -185,6 +186,10 @@ func (tx *Tx) Commit() error {
// If the high water mark has moved up then attempt to grow the database.
if tx.meta.pgid > opgid {
_ = errors.New("")
// gofail: var lackOfDiskSpace string
// tx.rollback()
// return errors.New(lackOfDiskSpace)
if err := tx.db.grow(int(tx.meta.pgid+1) * tx.db.pageSize); err != nil {
tx.rollback()
return err
@@ -470,6 +475,7 @@ func (tx *Tx) write() error {
// Ignore file sync if flag is set on DB.
if !tx.db.NoSync || IgnoreNoSync {
// gofail: var beforeSyncDataPages struct{}
if err := fdatasync(tx.db); err != nil {
return err
}
@@ -507,6 +513,7 @@ func (tx *Tx) writeMeta() error {
return err
}
if !tx.db.NoSync || IgnoreNoSync {
// gofail: var beforeSyncMetaPage struct{}
if err := fdatasync(tx.db); err != nil {
return err
}
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+616 -616
View File
File diff suppressed because it is too large Load Diff
+23 -23
View File
@@ -182,9 +182,9 @@ var (
// Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228.
CatmullRom = &Kernel{2, func(t float64) float64 {
if t < 1 {
return (1.5*t-2.5)*t*t + 1
return float64((float64(1.5*t)-2.5)*t*t) + 1
}
return ((-0.5*t+2.5)*t-4)*t + 2
return float64((float64(float64(float64(-0.5*t)+2.5)*t)-4)*t) + 2
}}
// TODO: a Kaiser-Bessel kernel?
@@ -247,7 +247,7 @@ func newDistrib(q *Kernel, dw, sw int32) distrib {
// source column or row.
n, sources := int32(0), make([]source, dw)
for x := range sources {
center := (float64(x)+0.5)*scale - 0.5
center := float64((float64(x)+0.5)*scale) - 0.5
i := int32(math.Floor(center - halfWidth))
if i < 0 {
i = 0
@@ -302,7 +302,7 @@ func abs(f float64) float64 {
// ftou converts the range [0.0, 1.0] to [0, 0xffff].
func ftou(f float64) uint16 {
i := int32(0xffff*f + 0.5)
i := int32(float64(0xffff*f) + 0.5)
if i > 0xffff {
return 0xffff
}
@@ -332,12 +332,12 @@ func fffftou(f float64) uint16 {
func invert(m *f64.Aff3) f64.Aff3 {
m00 := +m[3*1+1]
m01 := -m[3*0+1]
m02 := +m[3*1+2]*m[3*0+1] - m[3*1+1]*m[3*0+2]
m02 := +float64(m[3*1+2]*m[3*0+1]) - float64(m[3*1+1]*m[3*0+2])
m10 := -m[3*1+0]
m11 := +m[3*0+0]
m12 := +m[3*1+0]*m[3*0+2] - m[3*1+2]*m[3*0+0]
m12 := +float64(m[3*1+0]*m[3*0+2]) - float64(m[3*1+2]*m[3*0+0])
det := m00*m11 - m10*m01
det := float64(m00*m11) - float64(m10*m01)
return f64.Aff3{
m00 / det,
@@ -351,12 +351,12 @@ func invert(m *f64.Aff3) f64.Aff3 {
func matMul(p, q *f64.Aff3) f64.Aff3 {
return f64.Aff3{
p[3*0+0]*q[3*0+0] + p[3*0+1]*q[3*1+0],
p[3*0+0]*q[3*0+1] + p[3*0+1]*q[3*1+1],
p[3*0+0]*q[3*0+2] + p[3*0+1]*q[3*1+2] + p[3*0+2],
p[3*1+0]*q[3*0+0] + p[3*1+1]*q[3*1+0],
p[3*1+0]*q[3*0+1] + p[3*1+1]*q[3*1+1],
p[3*1+0]*q[3*0+2] + p[3*1+1]*q[3*1+2] + p[3*1+2],
float64(p[3*0+0]*q[3*0+0]) + float64(p[3*0+1]*q[3*1+0]),
float64(p[3*0+0]*q[3*0+1]) + float64(p[3*0+1]*q[3*1+1]),
float64(p[3*0+0]*q[3*0+2]) + float64(p[3*0+1]*q[3*1+2]) + p[3*0+2],
float64(p[3*1+0]*q[3*0+0]) + float64(p[3*1+1]*q[3*1+0]),
float64(p[3*1+0]*q[3*0+1]) + float64(p[3*1+1]*q[3*1+1]),
float64(p[3*1+0]*q[3*0+2]) + float64(p[3*1+1]*q[3*1+2]) + p[3*1+2],
}
}
@@ -371,8 +371,8 @@ func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
for i, p := range ps {
sxf := float64(p.X)
syf := float64(p.Y)
dx := int(math.Floor(s2d[0]*sxf + s2d[1]*syf + s2d[2]))
dy := int(math.Floor(s2d[3]*sxf + s2d[4]*syf + s2d[5]))
dx := int(math.Floor(float64(s2d[0]*sxf) + float64(s2d[1]*syf) + s2d[2]))
dy := int(math.Floor(float64(s2d[3]*sxf) + float64(s2d[4]*syf) + s2d[5]))
// The +1 adjustments below are because an image.Rectangle is inclusive
// on the low end but exclusive on the high end.
@@ -428,8 +428,8 @@ func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *i
d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
sx0 := int(float64(d2s[0]*dxf)+float64(d2s[1]*dyf)+d2s[2]) + bias.X
sy0 := int(float64(d2s[3]*dxf)+float64(d2s[4]*dyf)+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
@@ -450,8 +450,8 @@ func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *i
dyf := float64(dr.Min.Y+int(dy)) + 0.5
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
sx0 := int(float64(d2s[0]*dxf)+float64(d2s[1]*dyf)+d2s[2]) + bias.X
sy0 := int(float64(d2s[3]*dxf)+float64(d2s[4]*dyf)+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
@@ -479,8 +479,8 @@ func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *i
d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
sx0 := int(float64(d2s[0]*dxf)+float64(d2s[1]*dyf)+d2s[2]) + bias.X
sy0 := int(float64(d2s[3]*dxf)+float64(d2s[4]*dyf)+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
@@ -505,8 +505,8 @@ func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *i
dyf := float64(dr.Min.Y+int(dy)) + 0.5
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
sx0 := int(float64(d2s[0]*dxf)+float64(d2s[1]*dyf)+d2s[2]) + bias.X
sy0 := int(float64(d2s[3]*dxf)+float64(d2s[4]*dyf)+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+2
View File
@@ -58,6 +58,7 @@ includes_Darwin='
#define _DARWIN_USE_64_BIT_INODE
#define __APPLE_USE_RFC_3542
#include <stdint.h>
#include <sys/stdio.h>
#include <sys/attr.h>
#include <sys/clonefile.h>
#include <sys/kern_control.h>
@@ -551,6 +552,7 @@ ccflags="$@"
$2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ &&
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ ||
$2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ ||
$2 ~ /^(CONNECT|SAE)_/ ||
$2 ~ /^FIORDCHK$/ ||
$2 ~ /^SIOC/ ||
$2 ~ /^TIOC/ ||

Some files were not shown because too many files have changed in this diff Show More