├── .gitignore ├── LICENSE ├── README.md ├── go-wolfram.go ├── go.mod └── tests └── wolfram_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Krognol 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-wolfram 2 | Single file library for the Wolfram Alpha API written in Golang without extra dependencies 3 | 4 | # Installing 5 | `go get github.com/Edw590/go-wolfram` 6 | 7 | # Example 8 | ```go 9 | package main 10 | 11 | import "github.com/Edw590/go-wolfram" 12 | 13 | func main() { 14 | //Initialize a new client 15 | c := &wolfram.Client{AppID:"your app id here"} 16 | 17 | //Get a result without additional parameters 18 | res, err := c.GetQueryResult("1+1", nil) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | //Iterate through the pods and subpods 25 | //and print out their title attributes 26 | for i := range res.Pods { 27 | println(res.Pods[i].Title) 28 | 29 | for j := range res.Pods[i].SubPods { 30 | println(res.Pods[i].SubPods[j].Title) 31 | } 32 | } 33 | } 34 | ``` 35 | ### Output 36 | 37 | ``` 38 | > go run main.go 39 | 40 | < Input 41 | < Result 42 | < Number name 43 | < Visual representation 44 | < Number line 45 | < Illustration 46 | ``` 47 | 48 | # Adding extra parameters 49 | 50 | ```go 51 | package main 52 | 53 | import "github.com/Krognol/go-wolfram" 54 | 55 | func main() { 56 | //Initialize a new client 57 | c := &wolfram.Client{AppID:"your app id here"} 58 | 59 | params := url.Values{} 60 | params.Set("assumption", "DateOrder_**Day.Month.Year--") 61 | 62 | //Get a result with additional parameters 63 | res, err := c.GetQueryResult("26-9-2016", params) 64 | 65 | if err != nil { 66 | panic(err) 67 | } 68 | 69 | //Iterate through the pods and subpods 70 | //and print out their title attributes 71 | for i := range res.Pods { 72 | println(res.Pods[i].Title) 73 | 74 | for j := range res.Pods[i].SubPods { 75 | println(res.Pods[i].SubPods[j].Title) 76 | } 77 | } 78 | } 79 | ``` 80 | 81 | ### Output 82 | 83 | ``` 84 | > go run main.go 85 | 86 | < Input interpretation 87 | < Date formats 88 | < Time difference from today (Friday, September 23, 2016) 89 | < Time in 2016 90 | < Observances for September 26 (Sweden) 91 | < Anniversaries for September 26, 2016 92 | < Daylight information for September 26 in Stockholm, Sweden 93 | < Phase of the Moon 94 | ``` 95 | -------------------------------------------------------------------------------- /go-wolfram.go: -------------------------------------------------------------------------------- 1 | package wolfram 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | "strconv" 11 | ) 12 | 13 | // The client, requires an App ID, which you can sign up for at https://developer.wolframalpha.com/ 14 | type Client struct { 15 | AppID string 16 | } 17 | 18 | type Json struct { 19 | QueryResult QueryResult `json:"queryresult"` 20 | } 21 | 22 | // The QueryResult is what you get back after a request 23 | type QueryResult struct { 24 | //true or false depending on whether the input could be successfully 25 | //understood. If false there will be no subelements 26 | Success bool `json:"success"` 27 | 28 | //true or false depending on whether a serious processing error occurred, 29 | //such as a missing required parameter. If true there will be no pod 30 | //content, just an sub-element. 31 | Error bool `json:"error"` 32 | 33 | //The number of pod elements 34 | NumPods int `json:"numpods"` 35 | 36 | //Categories and types of data represented in the results 37 | DataTypes string `json:"datatypes"` 38 | 39 | //The number of pods that are missing because they timed out (see the 40 | //scantimeout query parameter). 41 | TimedOut string `json:"timedout"` 42 | 43 | TimedOutPods string `json:"timedoutpods"` 44 | 45 | //The wall-clock time in seconds required to generate the output. 46 | Timing float64 `json:"timing"` 47 | 48 | //The time in seconds required by the parsing phase. 49 | ParseTiming float64 `json:"parsetiming"` 50 | 51 | //Whether the parsing stage timed out (try a longer parsetimeout parameter 52 | //if true) 53 | ParseTimedOut bool `json:"parsetimedout"` 54 | 55 | //A URL to use to recalculate the query and get more pods. 56 | ReCalculate string `json:"recalculate"` 57 | 58 | //These elements are not documented currently 59 | ID string `json:"id"` 60 | Host string `json:"host"` 61 | Server string `json:"server"` 62 | Related string `json:"related"` 63 | 64 | //The version specification of the API on the server that produced this result. 65 | Version string `json:"version"` 66 | 67 | InputString string `json:"inputstring"` 68 | 69 | //The pods are what hold the majority of the information 70 | Pods []Pod `json:"pods"` 71 | 72 | //Each Source contains a link to a web page with the source information 73 | // EDIT: it's variable. Can be an array or an object alone. Then this fails sometimes and sometimes not. So I 74 | // disabled it. 75 | //Sources []Source `json:"sources"` 76 | } 77 | 78 | type Generalization struct { 79 | Topic string `json:"topic"` 80 | Description string `json:"desc"` 81 | URL string `json:"url"` 82 | } 83 | 84 | type Warnings struct { 85 | //How many warnings were issued 86 | Count int `json:"count"` 87 | 88 | //Suggestions for spelling corrections 89 | Spellchecks []Spellcheck `json:"spellcheck"` 90 | 91 | //"If you enter a query with mismatched delimiters like "sin(x", Wolfram|Alpha attempts to fix the problem and reports 92 | //this as a warning." 93 | Delimiters []Delimiters `json:"delimiters"` 94 | 95 | //"[The API] will translate some queries from non-English languages into English. In some cases when it does 96 | //this, you will get a element in the API result." 97 | Translations []Translation `json:"translation"` 98 | 99 | //"[The API] can automatically try to reinterpret a query that it does not understand but that seems close to one 100 | //that it can." 101 | ReInterpretations []ReInterpretation `json:"reinterpret"` 102 | } 103 | 104 | type Spellcheck struct { 105 | Word string `json:"word"` 106 | Suggestion string `json:"suggestion"` 107 | Text string `json:"text"` 108 | } 109 | 110 | type Delimiters struct { 111 | Text string `json:"text"` 112 | } 113 | 114 | type Translation struct { 115 | Phrase string `json:"phrase"` 116 | Translation string `json:"trans"` 117 | Language string `json:"lang"` 118 | Text string `json:"text"` 119 | } 120 | 121 | type ReInterpretation struct { 122 | Alternatives []Alternative `json:"alternative"` 123 | Text string `json:"text"` 124 | New string `json:"new"` 125 | } 126 | 127 | type Alternative struct { 128 | InnerText string `json:",innerxml"` 129 | } 130 | 131 | type Assumptions struct { 132 | Assumption []Assumption `json:"assumption"` 133 | Count int `json:"count"` 134 | } 135 | 136 | type Assumption struct { 137 | Values []Value `json:"value"` 138 | Type string `json:"type"` 139 | Word string `json:"word"` 140 | Template string `json:"template"` 141 | Count int `json:"count"` 142 | } 143 | 144 | // Usually contains info about an assumption 145 | type Value struct { 146 | Name string `json:"name"` 147 | Word string `json:"word"` 148 | Description string `json:"desc"` 149 | Input string `json:"input"` 150 | } 151 | 152 | // elements are subelements of . Each contains the results for a single pod 153 | type Pod struct { 154 | //The pod title, used to identify the pod. 155 | Title string `json:"title"` 156 | 157 | //The name of the scanner that produced this pod. A guide to the type of 158 | //data it holds. 159 | Scanner string `json:"scanner"` 160 | 161 | //Not documented currently 162 | ID string `json:"id"` 163 | Position int `json:"position"` 164 | Error bool `json:"error"` 165 | NumSubPods int `json:"numsubpods"` 166 | 167 | //Marks the pod that displays the closest thing to a simple "answer" that Wolfram|Alpha can provide 168 | Primary bool `json:"primary,omitempty"` 169 | 170 | //The subpod elements of the pod 171 | SubPods []SubPod `json:"subpods"` 172 | 173 | //sub elements of the pod 174 | States []State `json:"states"` 175 | } 176 | 177 | // If there was a sound related to the query, if you for example query a musical note 178 | // You will get a element which contains a link to the sound 179 | type Sounds struct { 180 | Count int `json:"count"` 181 | Sound []Sound `json:"sound"` 182 | } 183 | 184 | type Sound struct { 185 | URL string `json:"url"` 186 | Type string `json:"type"` 187 | } 188 | 189 | // If there's extra information for the pod, the pod will have a element 190 | // which contains elements with text, and/or images/links to that information 191 | type Infos struct { 192 | Count int `json:"count"` 193 | Info []Info `json:"info"` 194 | } 195 | 196 | type Info struct { 197 | Text string `json:"text"` 198 | Img []Img `json:"img"` 199 | Link []Link `json:"link"` 200 | } 201 | 202 | type Link struct { 203 | URL string `json:"url"` 204 | Text string `json:"text"` 205 | Title string `json:"title"` 206 | } 207 | 208 | // Source contains a link to a web page with the source information 209 | type Source struct { 210 | URL string `json:"url"` 211 | Text string `json:"text"` 212 | } 213 | 214 | // "Many pods on the Wolfram|Alpha website have text buttons in their upper-right corners that substitute the 215 | // contents of that pod with a modified version. In Figure 1, the Result pod has buttons titled "More days", "Sun and 216 | // Moon", CDT", "GMT", and "Show metric". Clicking any of these buttons will recompute just that one pod to display 217 | // different information." 218 | type State struct { 219 | Name string `json:"name"` 220 | Input string `json:"input"` 221 | StepByStep bool `json:"stepbystep"` 222 | ButtonStyle string `json:"buttonstyle"` 223 | } 224 | 225 | type SubPod struct { 226 | //Usually an empty string because most subpod elements don't have a title 227 | Title string `json:"title"` 228 | 229 | //HTML element 230 | Img Img `json:"img"` 231 | 232 | //Textual representation of the subpod 233 | Plaintext string `json:"plaintext"` 234 | } 235 | 236 | type Img struct { 237 | Src string `json:"src"` 238 | Alt string `json:"alt"` 239 | Title string `json:"title"` 240 | Width int `json:"width"` 241 | Height int `json:"height"` 242 | Type string `json:"type"` 243 | Themes string `json:"themes"` 244 | ColorInvertable bool `json:"colorinvertable"` 245 | ContentType string `json:"contenttype"` 246 | } 247 | 248 | // GetQueryResult gets the query result from the API and returns it. 249 | // Example extra parameter: "format=image", for a url.Value it'd be: 250 | // u := url.Values{} 251 | // u.Add("format", "image") 252 | // Additional information about parameters can be found at 253 | // http://products.wolframalpha.com/docs/WolframAlpha-API-Reference.pdf, page 42 254 | func (c *Client) GetQueryResult(query string, params url.Values) (*Json, error) { 255 | query = url.QueryEscape(query) 256 | 257 | url := fmt.Sprintf("https://api.wolframalpha.com/v2/query?input=%s&appid=%s&output=JSON", query, c.AppID) 258 | if params != nil { 259 | url += "&" + params.Encode() 260 | } 261 | 262 | data := &Json{} 263 | res, err := http.Get(url) 264 | if err != nil { 265 | return nil, err 266 | } 267 | err = unmarshal(res, data) 268 | 269 | return data, err 270 | } 271 | 272 | // Gets the json from the API and assigns the data to the target. 273 | // The target being a QueryResult struct 274 | func unmarshal(body *http.Response, target interface{}) error { 275 | defer body.Body.Close() 276 | return json.NewDecoder(body.Body).Decode(target) 277 | } 278 | 279 | // GetSimpleQuery gets an image from the `simple` endpoint. 280 | // 281 | // # Returns the image as a response body, the query url, and an error 282 | // 283 | // Can take some extra parameters, e.g `background=F5F5F5` 284 | // sets the background color to #F5F5F5 285 | // 286 | // The rest of the parameters can be found here https://products.wolframalpha.com/simple-api/documentation/ 287 | func (c *Client) GetSimpleQuery(query string, params url.Values) (io.ReadCloser, string, error) { 288 | query = url.QueryEscape(query) 289 | 290 | query = fmt.Sprintf("http://api.wolframalpha.com/v1/simple?appid=%s&input=%s&output=json", c.AppID, query) 291 | if params != nil { 292 | query += "&" + params.Encode() 293 | } 294 | 295 | res, err := http.Get(query) 296 | 297 | if err != nil { 298 | return nil, "", err 299 | } 300 | 301 | return res.Body, query, err 302 | } 303 | 304 | type Unit int 305 | 306 | const ( 307 | Imperial Unit = iota 308 | Metric 309 | ) 310 | 311 | func (c *Client) GetShortAnswerQuery(query string, units Unit, timeout int) (string, error) { 312 | query = url.QueryEscape(query) 313 | 314 | switch units { 315 | case Imperial: 316 | query += "&units=imperial" 317 | case Metric: 318 | query += "&units=metric" 319 | } 320 | 321 | if timeout != 0 { 322 | query += "&timeout=" + strconv.Itoa(timeout) 323 | } 324 | query = fmt.Sprintf("https://api.wolframalpha.com/v1/result?appid=%s&i=%s&output=json", c.AppID, query) 325 | res, err := http.Get(query) 326 | if err != nil { 327 | return "", err 328 | } 329 | 330 | defer res.Body.Close() 331 | b, err := ioutil.ReadAll(res.Body) 332 | if err != nil { 333 | return "", err 334 | } 335 | return string(b), nil 336 | } 337 | 338 | func (c *Client) GetSpokentAnswerQuery(query string, units Unit, timeout int) (string, error) { 339 | query = url.QueryEscape(query) 340 | 341 | switch units { 342 | case Imperial: 343 | query += "&units=imperial" 344 | case Metric: 345 | query += "&units=metric" 346 | } 347 | 348 | if timeout != 0 { 349 | query += "&timeout=" + strconv.Itoa(timeout) 350 | } 351 | query = fmt.Sprintf("https://api.wolframalpha.com/v1/spoken?appid=%s&i=%s&output=json", c.AppID, query) 352 | res, err := http.Get(query) 353 | if err != nil { 354 | return "", err 355 | } 356 | 357 | defer res.Body.Close() 358 | b, err := ioutil.ReadAll(res.Body) 359 | if err != nil { 360 | return "", err 361 | } 362 | return string(b), nil 363 | } 364 | 365 | type Mode int 366 | 367 | const ( 368 | Default Mode = iota 369 | Voice 370 | ) 371 | 372 | type FastQueryResult struct { 373 | Version string `json:"version"` 374 | SpellingCorrection string `json:"spellingCorretion"` 375 | BuildNumber string `json:"buildnumber"` 376 | Query []*struct { 377 | I string `json:"i"` 378 | Accepted string `json:"accepted"` 379 | Timing string `json:"timing"` 380 | Domain string `json:"domain"` 381 | ResultSignificanceScore string `json:"resultsignificancescore"` 382 | SummaryBox *struct { 383 | Path string `json:"path"` 384 | } `json:"summarybox"` 385 | } `json:"query"` 386 | } 387 | 388 | func (c *Client) GetFastQueryRecognizer(query string, mode Mode) (*FastQueryResult, error) { 389 | query = url.QueryEscape(query) 390 | 391 | switch mode { 392 | case Default: 393 | query += "&mode=Default" 394 | case Voice: 395 | query += "&mode=Voice" 396 | } 397 | 398 | query = fmt.Sprintf("https://www.wolframalpha.com/queryrecognizer/query.jsp?appid=%s&i=%s&output=json", c.AppID, query) 399 | 400 | res, err := http.Get(query) 401 | if err != nil { 402 | return nil, err 403 | } 404 | 405 | qres := &FastQueryResult{} 406 | err = unmarshal(res, qres) 407 | if err != nil { 408 | return nil, err 409 | } 410 | return qres, nil 411 | } 412 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Edw590/go-wolfram 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /tests/wolfram_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Krognol/go-wolfram" 7 | ) 8 | 9 | const APP_ID = "some appid" 10 | 11 | func TestGetQueryResult(t *testing.T) { 12 | c := &wolfram.Client{AppID: APP_ID} 13 | 14 | _, err := c.GetQueryResult("What is the price of gold?", nil) 15 | if err != nil { 16 | t.Failed() 17 | t.Log(err.Error()) 18 | } 19 | } 20 | 21 | func TestGetSimpleQueryResult(t *testing.T) { 22 | c := &wolfram.Client{AppID: APP_ID} 23 | 24 | _, _, err := c.GetSimpleQuery("What is the price of gold?", nil) 25 | if err != nil { 26 | t.Failed() 27 | t.Log(err.Error()) 28 | } 29 | } 30 | 31 | func TestGetFastQueryRecognizerResult(t *testing.T) { 32 | c := &wolfram.Client{AppID: APP_ID} 33 | 34 | _, err := c.GetFastQueryRecognizer("Gold price", wolfram.Default) 35 | if err != nil { 36 | t.Failed() 37 | t.Log(err.Error()) 38 | } 39 | } 40 | 41 | func TestGetShortAnswerQueryResult(t *testing.T) { 42 | c := &wolfram.Client{AppID: APP_ID} 43 | 44 | _, err := c.GetShortAnswerQuery("Price of gold", wolfram.Metric, 0) 45 | if err != nil { 46 | t.Failed() 47 | t.Log(err.Error()) 48 | } 49 | } 50 | 51 | func TestGetSpokenAnswerResult(t *testing.T) { 52 | c := &wolfram.Client{AppID: APP_ID} 53 | 54 | _, err := c.GetSpokentAnswerQuery("Price of gold", wolfram.Metric, 0) 55 | if err != nil { 56 | t.Failed() 57 | t.Log(err.Error()) 58 | } 59 | } 60 | --------------------------------------------------------------------------------