mailgun / mailgun-go Goto Github PK
View Code? Open in Web Editor NEWGo library for sending mail with the Mailgun API.
License: BSD 3-Clause "New" or "Revised" License
Go library for sending mail with the Mailgun API.
License: BSD 3-Clause "New" or "Revised" License
The current mailgun-go requires two keys to be specified:
Mailgun has recently (?) changed the names of these keys, so the "Public API Key" is now called "Email Validation Key". Also, they call the "API Key" "Active API Key".
Maybe the docs should be updated to reflect this?
And it would be good if it was enough to specify only the "API Key" if validation is not used in the mailgun-go API.
// from a webhook handler
url := r.PostFormValue("message-url")
urlp := strings.Split(url, "/")
err := mailgun.Client.GetStoredMessage(urlp[len(urlp)-1])
Generates an error that reads as
UnexpectedResponseError
URL=https://api.mailgun.net/v2/domains/mydomain.com/messages/eyJwIjpmYWxzZSaWFwiayZDgxNzc0LWJaWFlMzItNGYS1hM2E2LTc4NzUwYWRhZjMaWF3OCIsInMiOiJkYzRjZmIiwiYyI6InNhaWFkIn0=
ExpectedOneOf=[]int{200, 202, 204}
Got=400
Error: {"message":"Please use URLs as they are given in events/webhooks"}
While the value of url
was https://si.api.mailgun.net/v3/domains/mydomain.com/messages/eyJwIjpmYWxzZSaWFwiayZDgxNzc0LWJaWFlMzItNGYS1hM2E2LTc4NzUwYWRhZjMaWF3OCIsInMiOiJkYzRjZmIiwiYyI6InNhaWFkIn0=
.
Hi,
I would like to remove unsubscribes by tag. I have created a pull request for this: #55
PTAL.
Emails send fine. Very small attachments under ~200 characters send as well.
However any attachment over ~200 characters return the following error:
Post https://api.mailgun.net/v2/sandbox-removed-.mailgun.org/messages: EOF
The way mailgun/mailgun-go interacts with the mbanzon/simplehttp library can sometimes result in a whole application crash (SEGFAULT) when validating email, stacktrace follows.
The way I am calling the mg.ValidateEmail function is as follows: emailvalid, _ := mg.ValidateEmail(email) // (email is a sanitised string)
This issue is paired with another issue created on the mbanzon/simplehttp repository - https://github.com/mbanzon/simplehttp/issues/8
Stacktrace:
unexpected fault address 0x7f7500000011
fatal error: fault
[signal 0xb code=0x1 addr=0x7f7500000011]
goroutine 16 [running]:
:0
...
:0
goroutine 1 [IO wait]:
main.main
/root/gocode/src/mlc/controller.go:293 // This is just a simple log.Fatal(http.ListenAndServe("127.0.0.1:2369", m))
goroutine 4 [chan receive]:
goroutine 10 [select]:
github.com_mbanzon_simplehttp.MakeRequest.pN41_github.com_mbanzon_simplehttp.HTTPRequest
/root/gocode/src/github.com/mbanzon/simplehttp/simplehttp.go:108
github.com_mbanzon_simplehttp.MakeGetRequest.pN41_github.com_mbanzon_simplehttp.HTTPRequest
/root/gocode/src/github.com/mbanzon/simplehttp/simplehttp.go:59
mailgun.getResponseFromJSON
/root/gocode/src/github.com/mailgun/mailgun-go/rest_shim.go:72
github.com_mailgun_mailgun_go.ValidateEmail.pN41_github.com_mailgun_mailgun_go.MailgunImpl
/root/gocode/src/github.com/mailgun/mailgun-go/email_validation.go:49
main.$nested0
/root/gocode/src/mlc/controller.go:138
github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
/root/gocode/src/github.com/codegangsta/inject/inject.go:102
github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:144
github.com_go_martini_martini.Invoke.N42_github.com_go_martini_martini.routeContext
/root/gocode/src/github.com/go-martini/martini/router.go:336
github.com_go_martini_martini.run.pN42_github.com_go_martini_martini.routeContext
/root/gocode/src/github.com/go-martini/martini/router.go:350
github.com_go_martini_martini.Handle.pN35_github.com_go_martini_martini.route
/root/gocode/src/github.com/go-martini/martini/router.go:229
github.com_go_martini_martini.Handle.pN36_github.com_go_martini_martini.router
/root/gocode/src/github.com/go-martini/martini/router.go:112
github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
/root/gocode/src/github.com/codegangsta/inject/inject.go:102
github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:144
github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:173
github.com_go_martini_martini.Next.pN37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:164
martini.$nested2
/root/gocode/src/github.com/go-martini/martini/recovery.go:140
github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
/root/gocode/src/github.com/codegangsta/inject/inject.go:102
github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:144
github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:173
github.com_go_martini_martini.Next.pN37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:164
martini.$nested0
/root/gocode/src/github.com/go-martini/martini/logger.go:25
github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
/root/gocode/src/github.com/codegangsta/inject/inject.go:102
github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:144
github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
/root/gocode/src/github.com/go-martini/martini/martini.go:173
github.com_go_martini_martini.ServeHTTP.pN37_github.com_go_martini_martini.Martini
/root/gocode/src/github.com/go-martini/martini/martini.go:69
github.com_go_martini_martini.ServeHTTP.N44_github.com_go_martini_martini.ClassicMartini
/root/gocode/src/github.com/go-martini/martini/martini.go:105
Hi,
I'm a bit fuzzy on this, but I'm assuming the domain is the SMTP server. I basically have MailHog running, and I want all my e-mails to go to MailHog during development, but I can't find where to set the SMTP server that I hit.
I'm wondering if it's the domain we set when we create the mailgun.
Thanks.
Currently I can't see a way to set a Reply-To address, which seems to be supported by the mailgun API.
The functions to create and update webhooks time out frequently.
Is there a specific reason why all attributes of a message are private and there are no accessor methods? Would be really helpful to be able to read these values. I'll submit a pull request if its welcome. Wanted to know the thinking behind this decision before doing it though.
Also, it seems that a text version of the message is required. Every time I try to send a message with just an HTML body I get "Message not valid". Another conscious decision? The Mailgun API doesn't suggest that this is required and to my knowledge it shouldn't be.
Implement event polling as described in the mailgun docs here
mg := mailgun.NewMailgunFromEnv()
it := mg.PollEvents(EventOptions{ThresholdAge: time.Minute, TimeRange: time.Hour})
var events []Event
// Blocks until new events appear
for it.Poll(&events) {
for _, event := range(events) {
fmt.Printf("Event %+v\n", event)
}
}
Exploring the Go API examples. I see that the message content is added two times in second example, first time as text (when using .newMessage()
) and second time as HTML (using .setHTML()
). How does this work?
Which of these contents will be sent in the email? or both? or is it something else?
func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewFormDataPayload()
if s != nil {
p.AddValue("subscribed", yesNo(*s))
}
bs, err := json.Marshal(newMembers)
if err != nil {
return err
}
fmt.Println(string(bs))
p.AddValue("members", string(bs))
_, err = makePostRequest(r, p)
return err
}
in the api document, no subscribed form value, you should change to same the api docuemnt. change subscribed to upsert。
p.AddValue("subscribed", yesNo(*s))
=>
p.AddValue("upsert", yesNo(s))
In acceptance.go
, package "testing" is imported, and so is imported by anything using the mailgun go package. (In particular, that showed up to me as the testing command line flags showing up in a main package that was using the mailgun package.)
Perhaps acceptance.go
should instead be acceptance_test.go
? It seems the functions within are only used by other *_test.go
files.
In the Bounce struct of bounces.go, the Mailgun API returns a Code of type Int or String.
To duplicate, issue gun.GetBounces(-1, -1). If any bounces in the first page of 100 results contain a string and int in the Bounce, you'll get a type error when the json library unmarshalls the Code result to the Bounce struct.
"Error: json: cannot unmarshal string into Go value of type int"
I suppose the function
func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
m := MailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey}
return &m
}
has a publicApiKey
because the API v2 uses it? I cannot find any reference to it at the documentation / website of Mailgun, and leaving it blank enables me to send the e-mail anyway.
What's its use? And why is it still there?
mailgun-go depends on simplehttp, whose App Engine support is unfortunately broken: https://github.com/mbanzon/simplehttp/issues/7
So mailgun-go isn't currently suitable for use on App Engine.
Releases should be versioned (tagged): http://semver.org/
Is the upgrade to API v3 planned? Or is this project abandoned?
I'm not sure if there are any changes, but the URL changed, so there should be a reason for that? (at least we should update the URL mailgun-go uses.
Already adjusted to
ulimit -n
65536
but still get lots of error:
Get https://api.mailgun.net/v2/address/parse?addresses=foo%40bar.com: dial tcp: lookup api.mailgun.net on 10.0.0.2:53: dial udp 10.0.0.2:53: too many open files
my codes:
func send(subject, body string, to ...string) error {
mg := mailgun.NewMailgun(mailgunDomain, mailgunPrivateKey, mailgunPublicKey)
addrs, err := parseAddrs(mg, to...)
if err != nil {
return err
}
m := mg.NewMessage(
mailgunFrom, // From
subject, // Subject
body, // Plain-text body
addrs..., // Recipients (vararg list)
)
_, _, err = mg.Send(m)
return err
}
func parseAddrs(mg mailgun.Mailgun, to ...string) ([]string, error) {
addrs, unparseable, err := mg.ParseAddresses(to...)
if err != nil {
return nil, err
}
for _, invalid := range unparseable {
fmt.Println(invalid)
// todo send slack alert
}
return addrs, nil
}
I'm trying to mock out mailgun so that all messages get captured and can later be introspected for correct application behavior.
Here's a snippet of the mock:
type MockMailGun struct {
SentMessages chan *mailgun.Message
// Embed the Mailgun interface. No idea what will happen if unimplemented methods are called.
mailgun.Mailgun
}
func (mmg *MockMailGun) Send(m *mailgun.Message) (string, string, error) {
defer func() {
mmg.SentMessages <- m
}()
return "", "", nil
}
But I'm running into a problem when I try to look at the messages to introspect previous calls that were made to Message.AddHeader()
, because there's no way to get the headers (or any other metadata about the message.
Here's what I'd like to be able to do:
func (mmg *MockMailGun) waitForNotification(nt NotificationType) {
message := <-mmg.SentMessages
if message.GetHeader("MyHeader") == "foo" {
// Can't do this, no way to get header from message
}
}
But there is no message.GetHeader()
available in the API.
Is there any recommended way of doing this kind of mock or being able to introspect messages?
Hopefully this is the right place to ask this, I didn't see a link to a forum or group .. but let me know if I should post this somewhere else.
@mbanzon ,
I've run into situations where a request against mailgun API would fail, but errors are not propegated back to the caller of the API, at least through the err return. I've confirmed that simplehttp is not, seemingly by design, addressing 4xx or 5xx responses as errors.
Should we be inserting an abstraction layer between mailgun-go and simplehttp that does return code validation? I'm of the opinion that this is a good idea, as it'd simplify the interface from the caller's perspective. Thoughts?
Hi,
would it be possible to add an example of sending email that has an html and a plaintext part?
thanks.
https://documentation.mailgun.com/api-tags.html
Currently i'm thinking something similar to #73
it := mg.ListTags(-1)
var page []Tag
for it.Next(&page) {
for _, tags := range(page) {
// Do stuff with tags
}
}
if it.Err() != nil {
log.Fatal(it.Err())
}
if err := mg.UpdateTag(Tag{Tag: "my-tag", Description: "describe my tag"}); err != nil {
log.Fatal(err)
}
The acceptance package can't be compiled without the proper build tag.
Some files are missing the build tag but requiring the function reqEnv (included in a file that has the build tag) making a standard build of the package fail.
When passing in the storage.key of an event to the GetStoredMessage function, I get something like:
UnexpectedResponseError
URL=https://api.mailgun.net/v3/domains/example.com/messages/STORAGEKEY
ExpectedOneOf=[]int{200, 202, 204} Got=400
Error: {"message":"Please use URLs as they are given in events/webhooks"}
The URL provided by the event's storage.url uses a slightly different domain:
https://so.api.mailgun.net/v3/domains/example.com/messages/STORAGEKEY
Which, if followed (with proper credentials) gives the proper response.
The only difference from the URL generated by GetStoredMessage is the "so." sub-sub-domain, for what that is worth.
Can I use mailgun-go CreateCampaign,UpdateCampaign etc Campaigns function ? It is deprecated?
Once initialized, for example in an init() function, is a MailGun safe for concurrent use by multiple goroutines?
Hi,
do you plan to implement a CI test environment like travis, wercker, ...?
It's free and it's easier to check the contributed PRs.
You could, for example, check for test coverage, formatting and many more.
If you want, I will contribute travis or wecker integration.
Thanks and
best regards
When processing a webhook or route, you need to verify that the POST is actually coming from Mailgun. Basically this code: https://github.com/riobard/go-mailgun/blob/master/webhook.go#L89-L102.
Relatedly (and perhaps blocking this) there is no code in this library for processing routes or webhooks. I'd submit a PR but I'm curious how you'd like to see that added to the library.
When trying to use this API from Google App engine gives the errors on m.Send
Post https://api.mailgun.net/v3/sandboxmysandbox.mailgun.org/messages: http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/
This heavily relates to #24.
IMHO dependencies to external libraries should be removed if possible. The only reason that mailgun-go
use simplehttp
is that I originally wrote both libraries in parallel.
Hiding simplehttp
and its implementation bring trouble when it is needed to use alternative http.Client
or http.Transport
(when writing application for GAE eg.).
seems the recent merge broke the build, go get github.com/mailgun/mailgun-go
puts out the error ../../go/src/github.com/mailgun/mailgun-go/rest_shim.go:34: undefined: simplehttp in simplehttp.HTTPResponse
Currently iterating through events looks like this.
ei := mg.NewEventIterator()
err := ei.GetFirstPage(GetEventsOptions{})
for {
if err != nil {
return "", err
}
if len(ei.Events()) == 0 {
break
}
for _, event := range ei.Events() {
// Do things with **events**
}
err = ei.GetNext()
}
I wish to change this to a more golang idiomatic
it := mg.ListEvents(EventsOptions{})
var events []Event
for it.Next(&events) {
for _, event := range events {
// Do things with **events**
}
}
if it.Err() != nil {
return "", it.Err()
}
I bring this up because I'm currently implementing support for GET /v3/{domain}/tags
which also would use an iterator as eventually we expect customers to have lots of tags. I would love to see a consistent iterator interface for the user.
I'm implementing a dashboard for mailgun (https://github.com/Xotelia/mailgun-dashboard). I have a question regarding the event pagination. There is a next and previous link in event API response. But with the implementation of the API I have to request the first page and then, if we are on page ten I will have to call GetNext() 10 times. Is it possible to pass an offset to GetEventsOptions
? And also, how the pagination works ? based on timestamp? Offset? whatever?
I have a background process that periodically polls for unsubscribe events. I noticed in the log that it appears to be processing the same addresses each time it runs, even though I'm calling RemoveUnsubscribe with the unsubscribe events' IDs.
Perhaps I'm doing something incorrectly, or I've misunderstood these calls.
Any help is appreciated.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.