Vendored dependency
This commit is contained in:
Generated
+15
@@ -0,0 +1,15 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sadbox/mediawiki"
|
||||
packages = ["."]
|
||||
revision = "39fea8a1336076a961a300d1d95765dcd17e8a3c"
|
||||
version = "v0.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "875ef87fe71f1595d9be3e43d2eba538fdb3c449361cf31b521971c297745867"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sadbox/mediawiki"
|
||||
version = "0.1.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
/examples/examples
|
||||
/examples/config.xml
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 James McGuire
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
go-mediawiki [](https://drone.io/github.com/sadbox/go-mediawiki/latest)
|
||||
========
|
||||
A MediaWiki API wrapper for [Go](http://golang.org/)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Documentation specific to this implementaiton is [located on GoDoc](http://godoc.org/github.com/sadbox/go-mediawiki)
|
||||
|
||||
Documentation specific to the Mediawiki API in general is located [at their own wiki](http://www.mediawiki.org/wiki/API:Main_page)
|
||||
|
||||
Examples are located in the [examples subdirectory.](/examples)
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
- ☑ Login/Logout
|
||||
- ☑ Edit Pages
|
||||
- ☑ Read Pages
|
||||
- ☑ Upload
|
||||
- ☑ Download
|
||||
- ☑ Generic API Interface
|
||||
- ☑ Unit tests
|
||||
|
||||
Useful Links
|
||||
------------
|
||||
For easy handling of items that you get back from client.API()
|
||||
|
||||
https://github.com/jmoiron/jsonq
|
||||
|
||||
For adding your own structs
|
||||
|
||||
https://github.com/str1ngs/jflect
|
||||
|
||||
License
|
||||
-------
|
||||
This software is licensed under the MIT license. Pull requests and issues are welcome.
|
||||
+496
@@ -0,0 +1,496 @@
|
||||
// Copyright 2013 James McGuire
|
||||
// This code is covered under the MIT License
|
||||
// Please refer to the LICENSE file in the root of this
|
||||
// repository for any information.
|
||||
|
||||
// go-mediawiki provides a wrapper for interacting with the Mediawiki API
|
||||
//
|
||||
// Please see http://www.mediawiki.org/wiki/API:Main_page
|
||||
// for any API specific information or refer to any of the
|
||||
// functions defined for the MWApi struct for information
|
||||
// regarding this specific implementation.
|
||||
//
|
||||
// The client subdirectory contains an example application
|
||||
// that uses this API.
|
||||
package mediawiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The main mediawiki API struct, this is generated via mwapi.New()
|
||||
type MWApi struct {
|
||||
Username string
|
||||
Password string
|
||||
Domain string
|
||||
userAgent string
|
||||
debug bool
|
||||
url *url.URL
|
||||
client *http.Client
|
||||
format string
|
||||
edittoken string
|
||||
}
|
||||
|
||||
// This is used for passing data to the mediawiki API via key=value in a POST
|
||||
type Values map[string]string
|
||||
|
||||
// Unmarshal login data...
|
||||
type outerLogin struct {
|
||||
Login loginResponse
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Result string
|
||||
Token string
|
||||
}
|
||||
|
||||
// Unmarshall response from page edits...
|
||||
type outerEdit struct {
|
||||
Edit edit
|
||||
}
|
||||
|
||||
type edit struct {
|
||||
Result string
|
||||
PageId int
|
||||
Title string
|
||||
OldRevId int
|
||||
NewRevId int
|
||||
}
|
||||
|
||||
// General query response from mediawiki
|
||||
type mwQuery struct {
|
||||
Query query
|
||||
}
|
||||
|
||||
type query struct {
|
||||
Pages map[string]page
|
||||
}
|
||||
|
||||
type page struct {
|
||||
Pageid int
|
||||
Ns float64
|
||||
Title string
|
||||
Touched string
|
||||
Lastrevid float64
|
||||
// This will appear as both a string and a number... and the JSON unmarshaler
|
||||
// will crap out if this isn't set to a string.
|
||||
//Counter string
|
||||
Length float64
|
||||
Edittoken string
|
||||
Revisions []revision
|
||||
Imageinfo []image
|
||||
}
|
||||
|
||||
type revision struct {
|
||||
Body string `json:"*"`
|
||||
User string
|
||||
Timestamp string
|
||||
comment string
|
||||
}
|
||||
|
||||
type image struct {
|
||||
Url string
|
||||
Descriptionurl string
|
||||
}
|
||||
|
||||
type mwError struct {
|
||||
Error errorType
|
||||
}
|
||||
|
||||
type errorType struct {
|
||||
Code string
|
||||
Info string
|
||||
}
|
||||
|
||||
type uploadResponse struct {
|
||||
Upload uploadResult
|
||||
}
|
||||
|
||||
type uploadResult struct {
|
||||
Result string
|
||||
}
|
||||
|
||||
// Helper function for translating mediawiki errors in to golang errors.
|
||||
func CheckError(response []byte) error {
|
||||
var mwerror mwError
|
||||
err := json.Unmarshal(response, &mwerror)
|
||||
if err != nil {
|
||||
return nil
|
||||
} else if mwerror.Error.Code != "" {
|
||||
return errors.New(mwerror.Error.Code + ": " + mwerror.Error.Info)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new mediawiki API struct
|
||||
//
|
||||
// Example: mwapi.New("http://en.wikipedia.org/w/api.php", "My Mediawiki Bot")
|
||||
// Returns errors if the URL is invalid
|
||||
func New(wikiUrl, userAgent string) (*MWApi, error) {
|
||||
cookiejar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: nil,
|
||||
CheckRedirect: nil,
|
||||
Jar: cookiejar,
|
||||
}
|
||||
|
||||
clientUrl, err := url.Parse(wikiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MWApi{
|
||||
url: clientUrl,
|
||||
client: &client,
|
||||
format: "json",
|
||||
userAgent: "go-mediawiki https://github.com/sadbox/go-mediawiki " + userAgent,
|
||||
debug: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// This will automatically add the user agent and encode the http request properly
|
||||
func (m *MWApi) postForm(query url.Values) ([]byte, error) {
|
||||
request, err := http.NewRequest("POST", m.url.String(), strings.NewReader(query.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
request.Header.Set("user-agent", m.userAgent)
|
||||
resp, err := m.client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = CheckError(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// Download a file.
|
||||
//
|
||||
// Returns a readcloser that must be closed manually. Refer to the
|
||||
// example app for additional usage.
|
||||
func (m *MWApi) Download(filename string) (io.ReadCloser, error) {
|
||||
// First get the direct url of the file
|
||||
query := Values{
|
||||
"action": "query",
|
||||
"prop": "imageinfo",
|
||||
"iiprop": "url",
|
||||
"titles": filename,
|
||||
}
|
||||
|
||||
body, _, err := m.API(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response mwQuery
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fileurl string
|
||||
for _, page := range response.Query.Pages {
|
||||
if len(page.Imageinfo) < 1 {
|
||||
return nil, errors.New("No file found")
|
||||
}
|
||||
fileurl = page.Imageinfo[0].Url
|
||||
break
|
||||
}
|
||||
|
||||
// Then return the body of the response
|
||||
request, err := http.NewRequest("GET", fileurl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Set("user-agent", m.userAgent)
|
||||
|
||||
resp, err := m.client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
// Upload a file
|
||||
//
|
||||
// This does a simple, but more error-prone upload. Mediawiki
|
||||
// has a chunked upload version but it is only available in newer
|
||||
// versions of the API.
|
||||
//
|
||||
// Automatically retrieves an edit token if necessary.
|
||||
func (m *MWApi) Upload(dstFilename string, file io.Reader) error {
|
||||
if m.edittoken == "" {
|
||||
err := m.GetEditToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
query := Values{
|
||||
"action": "upload",
|
||||
"filename": dstFilename,
|
||||
"token": m.edittoken,
|
||||
"format": m.format,
|
||||
}
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(buffer)
|
||||
|
||||
for key, value := range query {
|
||||
err := writer.WriteField(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
part, err := writer.CreateFormFile("file", dstFilename)
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request, err := http.NewRequest("POST", m.url.String(), buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("Content-Type", "multipart/form-data; boundary="+writer.Boundary())
|
||||
request.Header.Set("user-agent", m.userAgent)
|
||||
|
||||
resp, err := m.client.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = CheckError(body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var response uploadResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Upload.Result == "Success" || response.Upload.Result == "Warning" {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New(response.Upload.Result)
|
||||
}
|
||||
}
|
||||
|
||||
// Login to the Mediawiki Website
|
||||
//
|
||||
// This will throw an error if you didn't define a username
|
||||
// or password.
|
||||
func (m *MWApi) Login() error {
|
||||
if m.Username == "" || m.Password == "" {
|
||||
return errors.New("Username or password not set.")
|
||||
}
|
||||
|
||||
query := Values{
|
||||
"action": "login",
|
||||
"lgname": m.Username,
|
||||
"lgpassword": m.Password,
|
||||
}
|
||||
|
||||
if m.Domain != "" {
|
||||
query["lgdomain"] = m.Domain
|
||||
}
|
||||
|
||||
body, _, err := m.API(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var response outerLogin
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Login.Result == "Success" {
|
||||
return nil
|
||||
} else if response.Login.Result != "NeedToken" {
|
||||
return errors.New("Error logging in: " + response.Login.Result)
|
||||
}
|
||||
|
||||
// Need to use the login token
|
||||
query["lgtoken"] = response.Login.Token
|
||||
|
||||
body, _, err = m.API(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Login.Result == "Success" {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Error logging in: " + response.Login.Result)
|
||||
}
|
||||
}
|
||||
|
||||
// Get an edit token
|
||||
//
|
||||
// This is necessary for editing any page.
|
||||
//
|
||||
// The Edit() function will call this automatically
|
||||
// but it is available if you want to make direct
|
||||
// calls to API().
|
||||
func (m *MWApi) GetEditToken() error {
|
||||
query := Values{
|
||||
"action": "query",
|
||||
"prop": "info|revisions",
|
||||
"intoken": "edit",
|
||||
"titles": "Main Page",
|
||||
}
|
||||
|
||||
body, _, err := m.API(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var response mwQuery
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, value := range response.Query.Pages {
|
||||
m.edittoken = value.Edittoken
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log out of the mediawiki website
|
||||
func (m *MWApi) Logout() {
|
||||
m.API(Values{"action": "logout"})
|
||||
}
|
||||
|
||||
// Edit a page
|
||||
//
|
||||
// This function will automatically grab an Edit Token if there
|
||||
// is not one currently stored.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// editConfig := mediawiki.Values{
|
||||
// "title": "SOME PAGE",
|
||||
// "summary": "THIS IS WHAT SHOWS UP IN THE LOG",
|
||||
// "text": "THE ENTIRE TEXT OF THE PAGE",
|
||||
// }
|
||||
// err = client.Edit(editConfig)
|
||||
func (m *MWApi) Edit(values Values) error {
|
||||
if m.edittoken == "" {
|
||||
err := m.GetEditToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
query := Values{
|
||||
"action": "edit",
|
||||
"token": m.edittoken,
|
||||
}
|
||||
body, _, err := m.API(query, values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var response outerEdit
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Edit.Result == "Success" {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New(response.Edit.Result)
|
||||
}
|
||||
}
|
||||
|
||||
// Request a wiki page and it's metadata.
|
||||
func (m *MWApi) Read(pageName string) (*revision, error) {
|
||||
query := Values{
|
||||
"action": "query",
|
||||
"prop": "revisions",
|
||||
"titles": pageName,
|
||||
"rvlimit": "1",
|
||||
"rvprop": "content|timestamp|user|comment",
|
||||
}
|
||||
body, _, err := m.API(query)
|
||||
|
||||
var response mwQuery
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, page := range response.Query.Pages {
|
||||
return &page.Revisions[0], nil
|
||||
}
|
||||
return nil, errors.New("No revisions found")
|
||||
}
|
||||
|
||||
// A generic interface to the Mediawiki API
|
||||
// Refer to the mediawiki API reference for any information regarding
|
||||
// what to pass to this function
|
||||
//
|
||||
// This is used by all internal functions to interact with the API
|
||||
//
|
||||
// The second return is simply the json data decoded in to an empty interface
|
||||
// that can be used by something like https://github.com/jmoiron/jsonq
|
||||
func (m *MWApi) API(values ...Values) ([]byte, interface{}, error) {
|
||||
query := m.url.Query()
|
||||
for _, valuemap := range values {
|
||||
for key, value := range valuemap {
|
||||
query.Set(key, value)
|
||||
}
|
||||
}
|
||||
query.Set("format", m.format)
|
||||
body, err := m.postForm(query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var unmarshalto interface{}
|
||||
err = json.Unmarshal(body, &unmarshalto)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return body, unmarshalto, nil
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/AiteRP/wikiauth"
|
||||
"git.aiterp.net/aiterp/wikiauth"
|
||||
)
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user