1st ver
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package anticaptcha
|
||||
|
||||
import (
|
||||
"go.digitalcircle.com.br/open/replaycli-go/api"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *api.Cli
|
||||
}
|
||||
|
||||
func (c *Cli) HttpCli() *api.Cli {
|
||||
return c.cli
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
Site string `json:"site"`
|
||||
Data string `json:"data"`
|
||||
Img []byte `json:"img"`
|
||||
To int `json:"to"`
|
||||
}
|
||||
|
||||
func (c *Cli) Recaptchav2(site string, data string) (string, error) {
|
||||
ret := ""
|
||||
req := &Req{
|
||||
Site: site,
|
||||
Data: data,
|
||||
Img: nil,
|
||||
To: 300,
|
||||
}
|
||||
err := c.cli.HttpJsonPost("/ipc/anticaptcha/recaptchav2", req, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *Cli) Image2text(site string, data []byte) (string, error) {
|
||||
ret := ""
|
||||
req := &Req{
|
||||
Site: site,
|
||||
Img: data,
|
||||
To: 300,
|
||||
}
|
||||
err := c.cli.HttpJsonPost("/ipc/anticaptcha/image2text", req, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
ret := &Cli{cli: api.NewCli()}
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package chrome
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.digitalcircle.com.br/open/replaycli-go/api"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *api.Cli
|
||||
}
|
||||
|
||||
func (c *Cli) HttpCli() *api.Cli {
|
||||
return c.cli
|
||||
}
|
||||
|
||||
func (c *Cli) Start(to int) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/chrome/start?to=%d", to), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) StartHeadless() error {
|
||||
err := c.cli.HttpJsonGet("/ipc/chrome/startHeadless", nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) Stop() error {
|
||||
err := c.cli.HttpJsonGet("/ipc/chrome/stop", nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) New(url string) (string, error) {
|
||||
ret := ""
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/chrome/new?url=%s", url), &ret)
|
||||
return ret, err
|
||||
}
|
||||
func (c *Cli) Close(id string) error {
|
||||
err := c.cli.HttpJsonGet("/ipc/chrome/close/"+id, nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) Eval(id string, s string) (map[string]interface{}, error) {
|
||||
ret := make(map[string]interface{})
|
||||
bs, err := c.cli.HttpRawPost("/ipc/chrome/eval/"+id, []byte(s))
|
||||
json.Unmarshal(bs, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *Cli) Wait(id string, s string, to int) (string, error) {
|
||||
ret := ""
|
||||
bs, err := c.cli.HttpRawPost(fmt.Sprintf("/ipc/chrome/wait/%s?to=%d", id, to), []byte(s))
|
||||
json.Unmarshal(bs, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *Cli) Send(id string, m string, ps map[string]interface{}) (string, error) {
|
||||
ret := ""
|
||||
in := map[string]interface{}{
|
||||
"method": m,
|
||||
"params": ps,
|
||||
}
|
||||
err := c.cli.HttpJsonPost("/ipc/chrome/eval/"+id, in, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
ret := &Cli{cli: api.NewCli()}
|
||||
return ret
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *http.Client
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
func (c *Cli) Addr() string {
|
||||
ret := os.Getenv("REPLAY_ADDR")
|
||||
if ret == "" {
|
||||
ret = "https://localhost:8443"
|
||||
} else if strings.HasPrefix(ret, ":") {
|
||||
ret = "https://localhost" + ret
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
func (c *Cli) AddHeader(k string, v string) {
|
||||
c.headers[k] = v
|
||||
}
|
||||
func (c *Cli) DelHeader(k string) {
|
||||
delete(c.headers, k)
|
||||
}
|
||||
func (c *Cli) HttpDo(method string, strurl string, body []byte) (*http.Response, error) {
|
||||
if !strings.HasPrefix(strurl, "http") {
|
||||
addr := c.Addr()
|
||||
if strings.HasSuffix(addr, "/") {
|
||||
addr = addr[:len(addr)-1]
|
||||
}
|
||||
if !strings.HasPrefix(strurl, "") {
|
||||
strurl = "/" + strurl
|
||||
}
|
||||
strurl = addr + strurl
|
||||
}
|
||||
req, err := http.NewRequest(method, strurl, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.headers != nil {
|
||||
for k, v := range c.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
if body != nil && len(body) > 0 {
|
||||
req, err = http.NewRequest(method, strurl, io.NopCloser(bytes.NewReader(body)))
|
||||
|
||||
} else {
|
||||
req, err = http.NewRequest(method, strurl, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-API-KEY", apikey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return c.cli.Do(req)
|
||||
}
|
||||
func (c *Cli) HttpDoJson(method string, strurl string, i interface{}, o interface{}) (err error) {
|
||||
bs, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := c.HttpDo(method, strurl, bs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if o != nil {
|
||||
err = json.NewDecoder(res.Body).Decode(o)
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c *Cli) HttpJsonGet(strurl string, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodGet, strurl, nil, o)
|
||||
}
|
||||
func (c *Cli) HttpJsonDelete(strurl string, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodDelete, strurl, nil, o)
|
||||
}
|
||||
func (c *Cli) HttpJsonHead(strurl string, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodHead, strurl, nil, o)
|
||||
}
|
||||
func (c *Cli) HttpJsonPost(strurl string, i interface{}, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodPost, strurl, i, o)
|
||||
}
|
||||
func (c *Cli) HttpJsonPut(strurl string, i interface{}, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodPut, strurl, i, o)
|
||||
}
|
||||
func (c *Cli) HttpJsonPatch(strurl string, i interface{}, o interface{}) error {
|
||||
return c.HttpDoJson(http.MethodPatch, strurl, i, o)
|
||||
}
|
||||
func (c *Cli) HttpRawGet(strurl string) ([]byte, error) {
|
||||
res, err := c.HttpDo(http.MethodGet, strurl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
bs, _ := io.ReadAll(res.Body)
|
||||
return bs, nil
|
||||
}
|
||||
func (c *Cli) HttpRawDelete(strurl string) ([]byte, error) {
|
||||
res, err := c.HttpDo(http.MethodDelete, strurl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
bs, _ := io.ReadAll(res.Body)
|
||||
return bs, nil
|
||||
}
|
||||
func (c *Cli) HttpRawPost(strurl string, i []byte) ([]byte, error) {
|
||||
res, err := c.HttpDo(http.MethodPost, strurl, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
bs, _ := io.ReadAll(res.Body)
|
||||
return bs, nil
|
||||
}
|
||||
func (c *Cli) HttpRawPut(strurl string, i []byte) ([]byte, error) {
|
||||
res, err := c.HttpDo(http.MethodPut, strurl, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
bs, _ := io.ReadAll(res.Body)
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
apikey = os.Getenv("REPLAY_APIKEY")
|
||||
ret := &Cli{
|
||||
cli: &http.Client{},
|
||||
headers: make(map[string]string),
|
||||
}
|
||||
ret.AddHeader("X-API-KEY", apikey)
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"go.digitalcircle.com.br/open/replaycli-go/api"
|
||||
)
|
||||
|
||||
const (
|
||||
CELLTYPE_STRING = "s"
|
||||
CELLTYPE_INT = "i"
|
||||
CELLTYPE_BOOL = "b"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *api.Cli
|
||||
}
|
||||
|
||||
func (c *Cli) HttpCli() *api.Cli {
|
||||
return c.cli
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
File string `json:"file"`
|
||||
Sheet string `json:"sheet"`
|
||||
Cel string `json:"cel"`
|
||||
Val string `json:"val"`
|
||||
Celtype string `json:"celtype"`
|
||||
}
|
||||
|
||||
func (c *Cli) Read(fname string, sheet string) ([][]string, error) {
|
||||
req := &Req{
|
||||
File: fname,
|
||||
Sheet: sheet,
|
||||
}
|
||||
res := make([][]string, 0)
|
||||
err := c.cli.HttpJsonPost("/ipc/excel/read", req, &res)
|
||||
return res, err
|
||||
}
|
||||
func (c *Cli) Write(fname string, sheet string, cell string, val string, celtype string) error {
|
||||
req := &Req{
|
||||
File: fname,
|
||||
Sheet: sheet,
|
||||
Cel: cell,
|
||||
Val: val,
|
||||
Celtype: celtype,
|
||||
}
|
||||
res := ""
|
||||
err := c.cli.HttpJsonPost("/ipc/excel/write", req, &res)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cli) New(fname string, sheet string) (string, error) {
|
||||
req := &Req{
|
||||
File: fname,
|
||||
Sheet: sheet,
|
||||
}
|
||||
res := ""
|
||||
err := c.cli.HttpJsonPost("/ipc/excel/new", req, &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
ret := &Cli{cli: api.NewCli()}
|
||||
return ret
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.digitalcircle.com.br/open/replaycli-go/types"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var apikey string
|
||||
|
||||
func init() {
|
||||
apikey = os.Getenv("REPLAY_APIKEY")
|
||||
}
|
||||
|
||||
func Addr() string {
|
||||
ret := os.Getenv("REPLAY_ADDR")
|
||||
if ret == "" {
|
||||
ret = "http://localhost:8080"
|
||||
} else if strings.HasPrefix(ret, ":") {
|
||||
ret = "http://localhost" + ret
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func HttpDo(method string, strurl string, body []byte) (*http.Response, error) {
|
||||
cli := http.Client{}
|
||||
var err error
|
||||
var req *http.Request
|
||||
|
||||
if body != nil && len(body) > 0 {
|
||||
req, err = http.NewRequest(method, strurl, io.NopCloser(bytes.NewReader(body)))
|
||||
|
||||
} else {
|
||||
req, err = http.NewRequest(method, strurl, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-API-KEY", apikey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return cli.Do(req)
|
||||
}
|
||||
|
||||
func HttpDoJson(i interface{}, method string, strurl string, body []byte) (err error) {
|
||||
res, err := HttpDo(method, strurl, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.NewDecoder(res.Body).Decode(i)
|
||||
return
|
||||
}
|
||||
|
||||
type roboDAO struct {
|
||||
}
|
||||
|
||||
func (d roboDAO) GetAll() ([]*types.Robot, error) {
|
||||
ret := make([]*types.Robot, 0)
|
||||
err := HttpDoJson(&ret, http.MethodGet, Addr()+fmt.Sprintf("/api/v1/robots"), nil)
|
||||
return ret, err
|
||||
}
|
||||
func (d roboDAO) Enqueue(id uint) error {
|
||||
_, err := HttpDo(http.MethodGet, Addr()+fmt.Sprintf("/api/v1/robots/op/enqueue/%d", id), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
var RoboDAO roboDAO
|
||||
|
||||
type sqlDAO struct {
|
||||
}
|
||||
|
||||
func (d sqlDAO) SQL(s string) (types.SQLResponse, error) {
|
||||
ret := types.SQLResponse{}
|
||||
req := types.SQLRequest{Sql: s}
|
||||
bs, _ := json.Marshal(req)
|
||||
err := HttpDoJson(&ret, http.MethodPost, Addr()+fmt.Sprintf("/api/v1/sql"), bs)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
var SQLDAO sqlDAO
|
||||
|
||||
type appDAO struct {
|
||||
}
|
||||
|
||||
func (d appDAO) GetAll() ([]*types.App, error) {
|
||||
ret := make([]*types.App, 0)
|
||||
err := HttpDoJson(&ret, http.MethodGet, Addr()+fmt.Sprintf("/api/v1/apps"), nil)
|
||||
return ret, err
|
||||
}
|
||||
func (d appDAO) Run(id interface{}) ([]*types.App, error) {
|
||||
ret := make([]*types.App, 0)
|
||||
err := HttpDoJson(&ret, http.MethodGet, Addr()+fmt.Sprintf("/api/v1/app/run/%v", id), nil)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
var AppDAO appDAO
|
||||
@@ -0,0 +1,89 @@
|
||||
package replay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"go.digitalcircle.com.br/open/replaycli-go/api"
|
||||
"go.digitalcircle.com.br/open/replaycli-go/types"
|
||||
"go.digitalcircle.com.br/open/replaycli-go/util"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *api.Cli
|
||||
}
|
||||
|
||||
func (c *Cli) HttpCli() *api.Cli {
|
||||
return c.cli
|
||||
}
|
||||
|
||||
func (c *Cli) OpenApp(id uint) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/api/v1/app/run/%d", id), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cli) ConfigGet(k string) (ret string, err error) {
|
||||
err = c.cli.HttpJsonGet("/api/v1/config/byrepo/"+util.Repo()+"/"+k, &ret)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cli) ConfigGetAll() (ret []types.Config, err error) {
|
||||
ret = make([]types.Config, 0)
|
||||
err = c.cli.HttpJsonGet("/api/v1/config/byrepo/"+util.Repo(), &ret)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cli) CronReload() error {
|
||||
return c.cli.HttpJsonGet("/ipc/cron/reload", nil)
|
||||
}
|
||||
|
||||
func (c *Cli) Exit() error {
|
||||
err := c.cli.HttpJsonGet("/api/v1/exit", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cli) MenuGetAllEnabled() (ret []types.Menu, err error) {
|
||||
ret = make([]types.Menu, 0)
|
||||
err = c.cli.HttpJsonGet("/api/v1/menu/enabled", nil)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *Cli) QueueAdd(job string, bs []byte) error {
|
||||
return c.cli.HttpJsonGet("/api/v1/queue/add/"+job, bs)
|
||||
}
|
||||
func (c *Cli) QueueGetData(id string) (ret []byte, err error) {
|
||||
ret, err = c.cli.HttpRawGet("/api/v1/queue/getrequest/" + id)
|
||||
return
|
||||
}
|
||||
func (c *Cli) QueueGetMyData() ([]byte, error) {
|
||||
return c.QueueGetData(os.Getenv("REPLAY_QUEUEID"))
|
||||
}
|
||||
func (c *Cli) QueueAbort() error {
|
||||
return c.cli.HttpJsonGet("/api/v1/queue/abort", nil)
|
||||
}
|
||||
func (c *Cli) QueueEnqueue(id uint) error {
|
||||
return c.cli.HttpJsonGet(fmt.Sprintf("/api/v1/robots/op/enqueue/%d", id), nil)
|
||||
}
|
||||
|
||||
func (c *Cli) ServiceStopAll() error {
|
||||
return c.cli.HttpJsonGet("/api/v1/service/op/stopall", nil)
|
||||
}
|
||||
|
||||
type SQLReturn struct {
|
||||
Data []map[string]interface{} `json:"data"`
|
||||
Err string `json:"err"`
|
||||
}
|
||||
|
||||
func (c *Cli) SQL(s string) (*SQLReturn, error) {
|
||||
in := &struct {
|
||||
Sql string `json:"sql"`
|
||||
}{s}
|
||||
out := &SQLReturn{}
|
||||
err := c.cli.HttpJsonPost("/api/v1/sql", in, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
ret := &Cli{cli: api.NewCli()}
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package wingui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.digitalcircle.com.br/open/replaycli-go/api"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cli *api.Cli
|
||||
}
|
||||
|
||||
func (c *Cli) HttpCli() *api.Cli {
|
||||
return c.cli
|
||||
}
|
||||
|
||||
func (c *Cli) ClipRead() (string, error) {
|
||||
ret := ""
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/clip/read"), &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *Cli) ClipWrite(site string) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/clip/write?str=%s", site), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cli) MouseClick() error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/mouse/click"), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) MouseMove(x, y int) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/mouse/move?x=%d&y=%d", x, y), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) MouseMoveRelative(x, y int) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/mouse/moverelative?x=%d&y=%d", x, y), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) MouseClickRelative(x, y int) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/mouse/clickatrelative?x=%d&y=%d", x, y), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) MouseClickAt(x, y int) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/mouse/clickat?x=%d&y=%d", x, y), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) ScreenClick(f string) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/click?f=%s", f), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) ScreenClickCenter(f string) error {
|
||||
err := c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/clickcenter?f=%s", f), nil)
|
||||
return err
|
||||
}
|
||||
func (c *Cli) ScreenFind(f string) (ret map[string]interface{}, err error) {
|
||||
ret = make(map[string]interface{})
|
||||
err = c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/find?f=%s", f), &ret)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cli) ScreenWait(f string, m int) (ret map[string]interface{}, err error) {
|
||||
ret = make(map[string]interface{})
|
||||
err = c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/wait?f=%s&m=%d", f, m), &ret)
|
||||
return
|
||||
}
|
||||
func (c *Cli) ScreenWaitClick(f string, m int) (ret map[string]interface{}, err error) {
|
||||
ret = make(map[string]interface{})
|
||||
err = c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/waitclick?f=%s&m=%d", f, m), &ret)
|
||||
return
|
||||
}
|
||||
func (c *Cli) ScreenWaitClickCenter(f string, m int) (ret map[string]interface{}, err error) {
|
||||
ret = make(map[string]interface{})
|
||||
err = c.cli.HttpJsonGet(fmt.Sprintf("/ipc/wingui/screen/waitclickcenter?f=%s&m=%d", f, m), &ret)
|
||||
return
|
||||
}
|
||||
|
||||
func NewCli() *Cli {
|
||||
ret := &Cli{cli: api.NewCli()}
|
||||
return ret
|
||||
}
|
||||
Reference in New Issue
Block a user