William Bowling is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this again1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /*
* Adium is the legal property of its developers, whose names are listed in the copyright file included
* with this source distribution.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import "AIAutoReplyPlugin.h"
#import <Adium/AIContactControllerProtocol.h>
#import <Adium/AIContentControllerProtocol.h>
#import "AIStatusController.h"
#import <AIUtilities/AIArrayAdditions.h>
#import <Adium/AIAccount.h>
#import <Adium/AIChat.h>
#import <Adium/AIContentMessage.h>
#import <Adium/AIStatus.h>
#import <Adium/AIContentObject.h>
/*!
* @class AIAutoReplyPlugin
* @brief Provides AutoReply functionality for the state system
*
* This class implements the state system behavior for auto-reply. If auto-reply status is active on an account,
* initial messages recieved on that account will be replied to automatically. Subsequent messages will not receive
* a reply unless the chat window is closed.
*
* This is the expected behavior on certain protocols such as AIM, and considered a convenience on other protocols.
*/
@implementation AIAutoReplyPlugin
/*!
* @brief Initialize the auto-reply system
*
* Initialize the auto-reply system to monitor account status. When an account auto-reply flag is set we begin to
* monitor chat messaging and auto-reply as necessary.
*/
- (void)installPlugin
{
//Init
receivedAutoReply = [[NSMutableSet alloc] init];
//Add observers
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveContent:)
name:CONTENT_MESSAGE_RECEIVED object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didSendContent:)
name:CONTENT_MESSAGE_SENT object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(chatWillClose:)
name:Chat_WillClose object:nil];
[[AIContactObserverManager sharedManager] registerListObjectObserver:self];
}
/*!
* Deallocate
*/
- (void)dealloc
{
[[AIContactObserverManager sharedManager] unregisterListObjectObserver:self];
[receivedAutoReply release];
[super dealloc];
}
/*!
* @brief Account status changed.
*
* Update our chat monitoring in response to account status changes.
*
* TODO: If there are no accounts with an auto-reply flag set we SHOULD stop monitoring messages for optimal performance.
*/
- (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
{
if ([inObject isKindOfClass:[AIAccount class]] &&
[inModifiedKeys containsObject:@"StatusState"]) {
//Reset our list of contacts who have already received an auto-reply
[receivedAutoReply release]; receivedAutoReply = [[NSMutableSet alloc] init];
//Don't want to remove from the new set any chats which previously closed and are pending removal
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
return nil;
}
/*!
* @brief Respond to a received message
*
* Respond to a received message by sending out the current auto-reply. We only send the auto-reply once to each
* contact, and then their name is added to a list and additional received messages are ignored.
*/
- (void)didReceiveContent:(NSNotification *)notification
{
AIContentObject *contentObject = [[notification userInfo] objectForKey:@"AIContentObject"];
AIChat *chat = contentObject.chat;
/* We will not respond to the received message if it is an auto-reply, over a chat where we have already responded,
* or over a group chat.
* Additionally, it's not sensible to send autoreplies if the arriving message
* was an offline message we're getting as we connect and it's older than 5 minutes
* (or the person sending it is no longer online) -RAF
* We also give the account a chance to suppress the autoreply
*/
if ([[contentObject type] isEqualToString:CONTENT_MESSAGE_TYPE] &&
![(AIContentMessage *)contentObject isAutoreply] &&
![receivedAutoReply containsObject:chat.uniqueChatID] &&
!chat.isGroupChat &&
(abs([contentObject.date timeIntervalSinceNow]) < 300) &&
[chat.account shouldSendAutoreplyToMessage:(AIContentMessage *)contentObject]) {
//300 is 5 minutes in seconds
[self sendAutoReplyFromAccount:(AIAccount *)contentObject.destination
toContact:contentObject.source
onChat:chat];
[receivedAutoReply addObject:chat.uniqueChatID];
}
}
/*!
* @brief Send an auto-reply
*
* Sends our current auto-reply to the specified contact.
* @param source Account sending the object
* @param destination Contact receiving the object
* @param chat Chat the communication is occuring over
*/
- (void)sendAutoReplyFromAccount:(AIAccount *)source toContact:(AIListObject *)destination onChat:(AIChat *)chat
{
AIContentMessage *responseContent = nil;
NSAttributedString *autoReply = chat.account.statusState.autoReply;
BOOL supportsAutoreply = source.supportsAutoReplies;
if (autoReply) {
if (!supportsAutoreply) {
//Tthe service isn't natively expecting an autoresponse, so make it a bit clearer what's going on
NSMutableAttributedString *mutableAutoReply = [[autoReply mutableCopy] autorelease];
[mutableAutoReply replaceCharactersInRange:NSMakeRange(0, 0)
withString:AILocalizedString(@"(Autoreply) ",
"Prefix to place before autoreplies on services which do not natively support them")];
autoReply = mutableAutoReply;
}
responseContent = [AIContentMessage messageInChat:chat
withSource:source
destination:destination
date:nil
message:autoReply
autoreply:supportsAutoreply];
[adium.contentController sendContentObject:responseContent];
}
}
/*!
* @brief Respond to our user sending messages
*
* For convenience, when our user messages a contact while away we exclude that contact from receiving our auto-away
* on future messages.
*/
- (void)didSendContent:(NSNotification *)notification
{
AIContentObject *contentObject = [[notification userInfo] objectForKey:@"AIContentObject"];
AIChat *chat = contentObject.chat;
if ([[contentObject type] isEqualToString:CONTENT_MESSAGE_TYPE]) {
[receivedAutoReply addObject:chat.uniqueChatID];
}
}
- (void)removeChatIDFromReceivedAutoReply:(id)uniqueChatID
{
[receivedAutoReply removeObject:uniqueChatID];
}
/*!
* @brief Respond to a chat closing
*
* Once a chat is closed we forget about whether it has received an auto-response. If the chat is re-opened, it will
* receive our auto-response again. This behavior is not necessarily desired, but is a side effect of basing our
* already-received list on chats and not contacts. However, many users have come to expect this behavior and it's
* presence is neither strongly negative or positive.
*/
- (void)chatWillClose:(NSNotification *)notification
{
/* Don't remove the chat until 30 seconds from now to prevent the classic situation in which you close the window,
* the contact messages you one last message, and a needless autoreply is sent
*/
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:@selector(removeChatIDFromReceivedAutoReply:)
object:[[notification object] uniqueChatID]];
[self performSelector:@selector(removeChatIDFromReceivedAutoReply:)
withObject:[[notification object] uniqueChatID]
afterDelay:30.0];
}
@end
|