How to get RSVP buttons through Icalendar gem - ruby-on-rails

So I've got the Icalendar gem in my ruby project. I'm attempting to get the RSVP buttons of Yes/No/Maybe on the invite but whenever it gets sent I only get a "Add to Calendar".
Was wondering what else I need:
def make_ical_appointment(start_time, end_time, uid, email_one, email_two)
ical = Icalendar::Calendar.new
ical.timezone.tzid = "UTC"
e = Icalendar::Event.new
e.dtstart = start_time
e.dtend = end_time
e.organizer = %W(mailto:#{email_one} mailto#{email_two})
e.uid = uid
ical.add_event(e)
ical.publish
mail.attachments['appointment.ics'] = { mime_type: 'application/ics', content: ical.to_ical }
end
I've read that people need to set it to METHOD:REQUEST, but I'm not sure where to do there. I've also read that you need to set attendees, but it seems you can only set attendees if you have an alarm?
Just looking to get it to look like a regular invite.

There's two things you need to do to solve your problem:
Read RFC-2445, which defines the iCal format. It looks like section 4.8.4.1, which discusses the ATTENDEE property, and 4.2.17, which discusses the RSVP parameter, will be of particular interest.
Look at emails and .ics files you've received that display correctly in various email clients.
The page I linked to in my comment above has three hints.
The first hint
I tried adding this property:calendar.custom_property("METHOD", "REQUEST").[1]
From the docs I think that's supposed to be append_custom_property.
Opening up an invite someone sent me from Google calendar, I found this line:
METHOD:REQUEST
So that seems legit.
The second hint
I would guess that you need to add an ATTENDEE property with RSVP=TRUE and the email set to the same email that Outlook or Yahoo link to their users.[2]
In the same invite I found this:
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
TRUE;CN=Firstname Lastname;X-NUM-GUESTS=0:mailto:jordan#example.com
I didn't read the whole RFC, but I think it breaks down like this:
ATTENDEE is the property name.
Everything between the first ; and the first : are parameters. Each of them are documented in the RFC, and I don't know if all of them are required, but we can see the RSVP=TRUE parameter there.
Everything after the first :, i.e. mailto:jordan#example.com is the value
Looking at the source of append_custom_property we see that it checks if value is an Icalendar::Value object, and if not it creates one with Icalendar::Values::Text.new(value). Since we have parameters in addition to a value, let's check out that constructor here. We see that it can take a second argument, which is a params Hash.
Now, I haven't tested it, but that suggests to me that you can build a line like the above with code something like the following†:
attendee_params = { "CUTYPE" => "INDIVIDUAL",
"ROLE" => "REQ-PARTICIPANT",
"PARTSTAT" => "NEEDS-ACTION",
"RSVP" => "TRUE",
"CN" => "Firstname Lastname",
"X-NUM-GUESTS" => "0" }
attendee_value = Icalendar::Values::Text.new("MAILTO:jordan#example.com", attendee_params)
ical.append_custom_property("ATTENDEE", attendee_value)
Edit: In Icalendar 2.x it looks like you can also do:
attendee_value = Icalendar::Values::CalAddress.new("MAILTO:jordan#example.com", attendee_params)
ical.append_attendee(attendee_value)
The CalAddress class is a subclass of Uri, which just runs the given value through URI.parse, and append_attendee appears to be a shortcut for append_custom_property("ATTENDEE", ...).
I'm not sure if all of those parameters are actually required, but you can learn what each of them is by reading the RFC.
The third hint
What I had to do to make it work in all mail clients was to send it as a multipart/alternative message with the ical as an alternative view instead of as an attachment.[3]
Sure enough, doing "Show Original" in Gmail I saw that the invite email I got is a multipart email, with a text/calendar part:
--047d7b0721581f7baa050a6c3dc0
Content-Type: text/calendar; charset=UTF-8; method=REQUEST
Content-Transfer-Encoding: 7bit
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
...
...and an application/ics attachment part:
--047d7b0721581f7bae050a6c3dc2
Content-Type: application/ics; name="invite.ics"
Content-Disposition: attachment; filename="invite.ics"
Content-Transfer-Encoding: base64
QkVHSU46VkNBTEVOREFSDQpQUk9ESUQ6LS8vR29vZ2xlIEluYy8vR29vZ2xlIENhbGVuZGFyIDcw
...
The second part you've already got, thanks to mail.attachments. For the first part, you just have to create a new Mail::Part with the correct content_type and add it to mail.parts, which will look something like this:
ical_part = Mail::Part.new do
content_type "text/calendar; charset=UTF-8; method=REQUEST"
body ical.to_ical
end
mail.add_part(ical_part)
That's all I've got. Again, I've tested none of this, and I'm not certain it'll fix your problem, but hopefully it gives you a few ideas.
The most important thing, I think, is to look at the source of emails (if you use Gmail, "Show Original" is under the drop-down menu next to the Reply button) with invites and look at how they're constructed, and likewise look at the .ics attachments and see whether or not they match what you're generating.
Good luck!
†Judging by the way Icalendar transforms the params hash into iCal parameters, I think you can use symbol keys, too, like so:
attendee_params = { cutype: "INDIVIDUAL",
role: "REQ-PARTICIPANT",
partstat: "NEEDS-ACTION",
rsvp: "TRUE",
cn: "Firstname Lastname",
x_num_guests: "0" }

Related

Gmail API, Reply to thread not working / forwarding

I'm using the google gmail api in swift. All is working well, it's compiling etc.
I'm now trying forward an email, the only way I see this possible so far is by using a thread id.
So I'm using the API tester found here to send tests. Will will focus on this. It can be found here1
So I've input this, the "raw" is Base64 URL encoded string.
{
"raw": "VG86ICBlbWFpbFRvU2VuZFRvQGdtYWlsLmNvbSAKU3ViamVjdDogIFRoZSBzdWJqZWN0IHRlc3QKSW4tUmVwbHktVG86ICBteUVtYWlsQGdtYWlsLmNvbQpUaHJlYWRJZDogIDE1YjkwYWU2MzczNDQ0MTIKClNvbWUgQ29vbCB0aGluZyBpIHdhbnQgdG8gcmVwbHkgdG8geW91ciBjb252by4u",
"threadId": "15b90ae637344412"
}
The "raw" in plain text is
To: emailToSendTo#gmail.com
Subject: The subject test
In-Reply-To: myEmail#gmail.com
ThreadId: 15b90ae637344412
Some Cool thing i want to reply to your convo..
when I execute it I get this back
{
"id": "15b944f6540396df",
"threadId": "15b90ae637344412",
"labelIds": [
"SENT"
]
}
But when I check both email account, from and to. None of them say the previous messages but are in the same "thread" or convo.
If anyone can help it would be much appreciated I've spent all day on this issue and half of yesterday and did TONS of research on it.
as stated here I should I'm adding the threaded and In-Reply-To in the right way I believe
The ID of the thread the message belongs to. To add a message or draft to a thread, the following criteria must be met:
The requested threadId must be specified on the Message or Draft.Message you supply with your request.
The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.
The Subject headers must match.

"Exception Occurred" with addSiteAccount1

After having gotten a site's login form from getSiteLoginForm, I'm attempting to add a site, but I'm receiving
{ :errorOccurred=>"true", :exceptionType=>"Exception Occurred", :referenceCode=>"_fa9ede97-1792-45ca-b147-005ec4002d33" }
The URL I'm POSTing to (in Rails) is:
https://consolidatedsdk.yodlee.com/yodsoap/srest/private-fairshare/v1.0/jsonsdk/SiteAccountManagement/addSiteAccount1
and this is the POST data:
cobSessionToken=REDACTED
userSessionToken=REDACTED
siteId=11671
credentialFields.enclosedType=com.yodlee.common.FieldInfoSingle
credentialFields[0][displayName]=User Name
credentialFields[0][fieldType.typeName]=TEXT
credentialFields[0][isEditable]=true
credentialFields[0][name]=LOGIN
credentialFields[0][value]=testuser
credentialFields[0][valueIdentifier]=LOGIN
credentialFields[0][valueMask]=LOGIN_FIELD
credentialFields[1][displayName]=Password
credentialFields[1][fieldType.typeName]=IF_PASSWORD
credentialFields[1][isEditable]=true
credentialFields[1][name]=PASSWORD
credentialFields[1][value]=testpass
credentialFields[1][valueIdentifier]=PASSWORD
credentialFields[1][valueMask]=LOGIN_FIELD
I've triple checked the parameters, and they seem to match up with the documentation.
Is there something I'm missing?
Looking at the documentation it looks like there's a mismatch in the format of your parameters.
For example, you have the field credentialFields[0][displayName], but in the documentation it's referred to as credentialFields[0].displayName. Is is possible that the API expects fields in this format?
If the API does expect fields in the credentialFields[0][displayName] then it would make sense for the credentialFields.enclosedType field to follow the same format. In that case it should be credentialFields[enclosedType].

Rails not sending (ical) attachments in emails

I'm trying to send an email with an ical attachment, I'm running rails v.3.2.13 and using the icalendar gem (to generate the ical string) see. (In development mode in case that might be a problem).
The relevant mailer code looks like this:
def mailme
ical = Icalendar::Calendar.new
...
attachments["meetings.ics"] = { mime_type: "text/calendar", content: ical.to_ical }
mail(from: email, to: recipient, ...)
end
there is also template file with the same name (mailme.html.erb)
The problem is the mail (html) is send without the attachment.
As usual any help would be much appreciated.
Thank you.
I've gotten them working with something like below:
mail.attachments['meeting.ics'] = { mime_type: 'application/ics',
content: ical.to_ical }
mail(from: email, to: recipient, ...)
So it's possible you need to call it on #attachments on the mail object instead of calling it on the current context. I'm not sure if your mime type needs to be application/ics, but that's worked fine for me in my systems.
In case someone else stumbles upon this.
If you are using delayed_job check out https://github.com/collectiveidea/delayed_job/wiki/Common-problems#wiki-Sending_emails_with_attachments
To fix this, remember to add this line to your mailer:
content_type "multipart/mixed"

Facebook Graph Feed Post Not Including Message

I have the following snippet to post to a user's feed on Facebook:
require 'httparty'
token = "..."
message = "..."
url = URI.escape("https://graph.facebook.com/me/feed?access_token=#{token}")
response = HTTParty.post(url, body: { message: message })
This posts to the wall, but no message is included. Any ideas what's wrong?
Edit:
I tried changing out the message for a caption or description and both failed as well.
Solution is to change HTTParty from using body to query for posting form data:
require 'httparty'
token = "..."
message = "..."
url = URI.escape("https://graph.facebook.com/me/feed?access_token=#{token}")
response = HTTParty.post(url, query: { message: message })
Based on the link cited above, it appears message functionality has been completely removed from the feed connection since July 12.
This is a problem for my current app as it is specifically a public opinion site. Asking users to express their opinions authentically is an important part of our design and we'd like to give them the option to post that to their feeds on Facebook as well.
Per the Facebook terms of use IV.2, "You must not pre-fill any of the fields associated with the following products, unless the user manually generated the content earlier in the workflow." The new change appears to change the terms of service: in my use, I am specifically asking the user to generate the content earlier in the workflow, but I still can't use it to pre-fill the feed dialog.
Anyone have any ideas or insight?

While processing an email reply how can I ignore any email client specifics & the history?

I have a rails application which processes incoming emails via IMAP. Currently a method is used that searches the parts of a TMail object for a given content_type:
def self.search_parts_for_content_type(parts, content_type = 'text/html')
parts.each do |part|
if part.content_type == content_type
return part.body
else
if part.multipart?
if body = self.search_parts_for_content_type(part.parts, content_type)
return body
end
end
end
end
return false
end
These emails are generally in response to a html email it sent out in the first place. (The original outbound email is never the same.) The body text the method above returns contains the full history of the email and I would like to just parse out the reply text.
I'm wondering whether it's reasonable to place some '---please reply above this line---' text at the top of the mail as I have seen in a 37 signals application.
Is there another way to ignore the client specific additions to the email, other than write a multitude of regular expressions (which I haven't yet attempted) for each and every mail client? They all seem to tack on their own bit at the top of any replies.
I have to do email reply parsing on a project I'm working on right now. I ended up using pattern matching to identify the response part, so users wouldn't have to worry about where to insert their reply.
The good news is that the implementation really isn't too difficult. The hard part is just testing all the different email clients and services you want to support and figuring out how to identify each one. Generally, you can use either the message ID or the X-Mailer or Return-Path header to determine where an incoming email came from.
Here's a method that takes a TMail object and extracts the response part of the message and returns that along with the email client/service it was sent from. It assumes you have the original message's From: name and address in the constants FROM_NAME and FROM_ADDRESS.
def find_reply(email)
message_id = email.message_id('')
x_mailer = email.header_string('x-mailer')
# For optimization, this list could be sorted from most popular to least popular email client/service
rules = [
[ 'Gmail', lambda { message_id =~ /.+gmail\.com>\z/}, /^.*#{FROM_NAME}\s+<#{FROM_ADDRESS}>\s*wrote:.*$/ ],
[ 'Yahoo! Mail', lambda { message_id =~ /.+yahoo\.com>\z/}, /^_+\nFrom: #{FROM_NAME} <#{FROM_ADDRESS}>$/ ],
[ 'Microsoft Live Mail/Hotmail', lambda { email.header_string('return-path') =~ /<.+#(hotmail|live).com>/}, /^Date:.+\nSubject:.+\nFrom: #{FROM_ADDRESS}$/ ],
[ 'Outlook Express', lambda { x_mailer =~ /Microsoft Outlook Express/ }, /^----- Original Message -----$/ ],
[ 'Outlook', lambda { x_mailer =~ /Microsoft Office Outlook/ }, /^\s*_+\s*\nFrom: #{FROM_NAME}.*$/ ],
# TODO: other email clients/services
# Generic fallback
[ nil, lambda { true }, /^.*#{FROM_ADDRESS}.*$/ ]
]
# Default to using the whole body as the reply (maybe the user deleted the original message when they replied?)
notes = email.body
source = nil
# Try to detect which email service/client sent this message
rules.find do |r|
if r[1].call
# Try to extract the reply. If we find it, save it and cancel the search.
reply_match = email.body.match(r[2])
if reply_match
notes = email.body[0, reply_match.begin(0)]
source = r[0]
next true
end
end
end
[notes.strip, source]
end
I think you will be stuck on this one. I have been doing some stuff with emails myself in TMail recently, and what you will generally find is that an email that has an HTML part is generally structured like:
part 1 - multipart/mixed
sub part 1 - text/plain
sub part 2 - text/html
end
The email clients I have played with Outlook and Gmail both generate replies in this format, and they just generally quote the original email inline in the reply. At first I though that the 'old' parts of the original email would be separate parts, but they are actually not - the old part is just merged into the reply part.
You could search the part for a line that begins 'From: ' (as most clients generally place a header at the top of the original email text detailing who sent it etc), but its probably not guaranteed.
I don't really see anything wrong with a --- please reply above this line --- generally, its not that invasive, and could make things a lot simpler.

Resources