Java IRC bot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ChannelInfo.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. /*
  2. * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.parser.irc;
  23. import java.util.ArrayList;
  24. import java.util.Hashtable;
  25. import java.util.HashMap;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Queue;
  30. /**
  31. * Contains Channel information.
  32. *
  33. * @author Shane Mc Cormack
  34. * @author Chris Smith
  35. * @see IRCParser
  36. */
  37. public class ChannelInfo {
  38. /**
  39. * Boolean repreenting the status of names requests.
  40. * When this is false, any new names reply will cause current known channelclients to be removed.
  41. */
  42. private boolean bAddingNames = true;
  43. /** Unixtimestamp representing time when the channel was created. */
  44. private long nCreateTime = 0;
  45. /** Current known topic in the channel. */
  46. private String sTopic = "";
  47. /** Last known user to set the topic (Full host where possible). */
  48. private String sTopicUser = "";
  49. /** Unixtimestamp representing time when the topic was set. */
  50. private long nTopicTime = 0;
  51. /** Known boolean-modes for channel. */
  52. private long nModes;
  53. /** Reference to the parser object that owns this channel, Used for modes. */
  54. private final IRCParser myParser; // Reference to parser object that owns this channel. Used for Modes
  55. /** Channel Name. */
  56. private final String sName;
  57. /** Hashtable containing references to ChannelClients. */
  58. private final Map<String, ChannelClientInfo> hChannelUserList = new Hashtable<String, ChannelClientInfo>();
  59. /** Hashtable storing values for modes set in the channel that use parameters. */
  60. private final Map<Character, String> hParamModes = new Hashtable<Character, String>();
  61. /** Hashtable storing list modes. */
  62. private final Map<Character, ArrayList<ChannelListModeItem>> hListModes = new Hashtable<Character, ArrayList<ChannelListModeItem>>();
  63. /**
  64. * LinkedList storing status of mode adding.
  65. * if an item is in this list for a mode, we are expecting new items for the list
  66. */
  67. private final List<Character> lAddingModes = new LinkedList<Character>();
  68. /** Modes waiting to be sent to the server. */
  69. private final List<String> lModeQueue = new LinkedList<String>();
  70. /** A Map to allow applications to attach misc data to this object */
  71. private Map myMap;
  72. /** Queue of requested list modes */
  73. private final Queue<Character> listModeQueue = new LinkedList<Character>();
  74. /** Listmode Queue Time */
  75. private long listModeQueueTime = System.currentTimeMillis();
  76. /** Have we asked the server for the list modes for this channel yet? */
  77. private boolean askedForListModes = false;
  78. /** Has OnChannelGotListModes ever been called for this channel? */
  79. private boolean hasGotListModes = false;
  80. /**
  81. * Create a new channel object.
  82. *
  83. * @param tParser Refernce to parser that owns this channelclient (used for modes)
  84. * @param name Channel name.
  85. */
  86. public ChannelInfo(final IRCParser tParser, final String name) {
  87. myMap = new HashMap<Object, Object>();
  88. myParser = tParser;
  89. sName = name;
  90. }
  91. /**
  92. * Get the listModeQueue.
  93. *
  94. * @return The listModeQueue
  95. */
  96. public Queue<Character> getListModeQueue() {
  97. Queue<Character> result = listModeQueue;
  98. final long now = System.currentTimeMillis();
  99. // Incase of breakage, if getListModeQueue() was last called greater than
  100. // 60 seconds ago, we reset the list.
  101. if (now-(30*1000) > listModeQueueTime) {
  102. result = new LinkedList<Character>();
  103. myParser.callDebugInfo(IRCParser.DEBUG_LMQ, "Resetting LMQ");
  104. }
  105. listModeQueueTime = now;
  106. return result;
  107. }
  108. /**
  109. * Ask the server for all the list modes for this channel.
  110. */
  111. public void requestListModes() {
  112. final ChannelClientInfo me = getUser(myParser.getMyself());
  113. if (me == null) {
  114. // In a normal situation of non bouncer-brokenness this won't happen
  115. return;
  116. }
  117. askedForListModes = true;
  118. final String thisIRCD = myParser.getIRCD(true).toLowerCase();
  119. final boolean isFreenode = (thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"));
  120. final boolean isUnreal = thisIRCD.equals("unreal");
  121. final boolean isStarChat = thisIRCD.equals("starchat");
  122. final boolean isHybrid = thisIRCD.equals("hybrid");
  123. final boolean isCharybdis = thisIRCD.equals("charybdis");
  124. // We are considered opped if we have a mode higher than voice (or if we have any modes if voice doesn't exist)
  125. long voiceValue = 0;
  126. if (myParser.hPrefixModes.get('v') != null) { voiceValue = myParser.hPrefixModes.get('v');}
  127. final boolean isOpped = me.getImportantModeValue() > voiceValue;
  128. int modecount = 1;
  129. if (!isUnreal) {
  130. try {
  131. modecount = Integer.parseInt(myParser.h005Info.get("MODES"));
  132. } catch (NumberFormatException e) { /* use default modecount */}
  133. }
  134. // Support for potential future decent mode listing in the protocol
  135. //
  136. // See my proposal: http://shane.dmdirc.com/listmodes.php
  137. // Add listmode handler
  138. final boolean supportLISTMODE = myParser.h005Info.containsKey("LISTMODE");
  139. String listmodes = "";
  140. int i = 0;
  141. for (Character cTemp : myParser.hChanModesOther.keySet()) {
  142. final int nTemp = myParser.hChanModesOther.get(cTemp);
  143. if (nTemp == IRCParser.MODE_LIST) {
  144. if ((isFreenode || isHybrid || isCharybdis) && (cTemp == 'e' || cTemp == 'I') && !isOpped) {
  145. // IRCD doesn't allow non-ops to ask for these modes.
  146. continue;
  147. } else if (isStarChat && cTemp == 'H') {
  148. // IRCD Denies the mode exists
  149. continue;
  150. }
  151. i++;
  152. listmodes = listmodes + cTemp;
  153. if (i >= modecount && !supportLISTMODE) {
  154. myParser.sendString("MODE "+getName()+" "+listmodes);
  155. i = 0;
  156. listmodes = "";
  157. }
  158. }
  159. }
  160. if (i > 0) {
  161. if (supportLISTMODE) {
  162. myParser.sendString("LISTMODE "+getName()+" "+listmodes);
  163. } else {
  164. myParser.sendString("MODE "+getName()+" "+listmodes);
  165. }
  166. }
  167. }
  168. /**
  169. * Have we ever asked the server for this channels listmodes?
  170. *
  171. * @return True if requestListModes() has ever been used, else false
  172. */
  173. public boolean hasAskedForListModes() {
  174. return askedForListModes;
  175. }
  176. /**
  177. * Returns true if OnChannelGotListModes ever been called for this channel.
  178. *
  179. * @return True if OnChannelGotListModes ever been called for this channel.
  180. */
  181. public boolean hasGotListModes() {
  182. return hasGotListModes;
  183. }
  184. /**
  185. * Set if OnChannelGotListModes ever been called for this channel.
  186. *
  187. * @param newValue new value for if OnChannelGotListModes ever been called for this channel.
  188. */
  189. protected void setHasGotListModes(final boolean newValue) {
  190. hasGotListModes = newValue;
  191. }
  192. /**
  193. * Set the Map object attatched to this object.
  194. *
  195. * @param newMap New Map to attatch.
  196. */
  197. public void setMap(final Map newMap) {
  198. myMap = newMap;
  199. }
  200. /**
  201. * Get the Map object attatched to this object.
  202. *
  203. * @return Map to attatched to this.
  204. */
  205. public Map getMap() {
  206. return myMap;
  207. }
  208. /**
  209. * Set if we are getting a names request or not.
  210. *
  211. * @param newValue if false, any new names reply will cause current known channelclients to be removed.
  212. */
  213. public void setAddingNames(final boolean newValue) { bAddingNames = newValue; }
  214. /**
  215. * Get if we are getting a names request or not.
  216. *
  217. * @return if false, any new names reply will cause current known channelclients to be removed.
  218. */
  219. public boolean isAddingNames() { return bAddingNames; }
  220. /**
  221. * Get the name of this channel object.
  222. *
  223. * @return Channel name.
  224. */
  225. public String getName() { return sName; }
  226. /**
  227. * Get the number of users known on this channel.
  228. *
  229. * @return Channel user count.
  230. */
  231. public int getUserCount() { return hChannelUserList.size(); }
  232. /**
  233. * Get the channel users.
  234. *
  235. * @return ArrayList of ChannelClients
  236. */
  237. public List<ChannelClientInfo> getChannelClients() {
  238. return new ArrayList<ChannelClientInfo>(hChannelUserList.values());
  239. }
  240. /**
  241. * Empty the channel (Remove all known channelclients).
  242. */
  243. protected void emptyChannel() {
  244. ClientInfo cTemp = null;
  245. for (ChannelClientInfo client : hChannelUserList.values()) {
  246. cTemp = client.getClient();
  247. cTemp.delChannelClientInfo(client);
  248. if (cTemp != myParser.getMyself() && !cTemp.checkVisibility()) {
  249. myParser.removeClient(cTemp);
  250. }
  251. }
  252. hChannelUserList.clear();
  253. }
  254. /**
  255. * Get the ChannelClientInfo object associated with a nickname.
  256. *
  257. * @param sWho Nickname to return channelclient for
  258. * @return ChannelClientInfo object requested, or null if not found
  259. */
  260. public ChannelClientInfo getUser(final String sWho) {
  261. return getUser(sWho, false);
  262. }
  263. /**
  264. * Get the ChannelClientInfo object associated with a nickname.
  265. *
  266. * @param sWho Nickname to return channelclient for
  267. * @param createFake Create a fake client if not found
  268. * @return ChannelClientInfo object requested
  269. * @since 0.6
  270. */
  271. public ChannelClientInfo getUser(final String sWho, final boolean createFake) {
  272. final String who = myParser.getIRCStringConverter().toLowerCase(ClientInfo.parseHost(sWho));
  273. if (hChannelUserList.containsKey(who)) {
  274. return hChannelUserList.get(who);
  275. }
  276. if (createFake) {
  277. return new ChannelClientInfo(myParser, (new ClientInfo(myParser, sWho)).setFake(true), this);
  278. } else {
  279. return null;
  280. }
  281. }
  282. /**
  283. * Get the ChannelClientInfo object associated with a ClientInfo object.
  284. *
  285. * @param cWho ClientInfo to return ChannelClient for
  286. * @return ChannelClientInfo object requested, or null if not found
  287. */
  288. public ChannelClientInfo getUser(final ClientInfo cWho) {
  289. for (ChannelClientInfo client : hChannelUserList.values()) {
  290. if (client.getClient() == cWho) {
  291. return client;
  292. }
  293. }
  294. return null;
  295. }
  296. /**
  297. * Get the ChannelClientInfo object associated with a ClientInfo object.
  298. *
  299. * @param cClient Client object to be added to channel
  300. * @return ChannelClientInfo object added, or an existing object if already known on channel
  301. */
  302. protected ChannelClientInfo addClient(final ClientInfo cClient) {
  303. ChannelClientInfo cTemp = getUser(cClient);
  304. if (cTemp == null) {
  305. cTemp = new ChannelClientInfo(myParser, cClient, this);
  306. hChannelUserList.put(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()), cTemp);
  307. }
  308. return cTemp;
  309. }
  310. /**
  311. * Remove ChannelClientInfo object associated with a ClientInfo object.
  312. *
  313. * @param cClient Client object to be removed from channel
  314. */
  315. protected void delClient(final ClientInfo cClient) {
  316. ChannelClientInfo cTemp = null;
  317. cTemp = getUser(cClient);
  318. if (cTemp != null) {
  319. final ClientInfo clTemp = cTemp.getClient();
  320. clTemp.delChannelClientInfo(cTemp);
  321. if (clTemp != myParser.getMyself() && !clTemp.checkVisibility()) {
  322. myParser.removeClient(clTemp);
  323. }
  324. hChannelUserList.remove(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()));
  325. }
  326. }
  327. /**
  328. * Rename a channelClient.
  329. *
  330. * @param oldNickname Nickname client used to be known as
  331. * @param cChannelClient ChannelClient object with updated client object
  332. */
  333. protected void renameClient(final String oldNickname, final ChannelClientInfo cChannelClient) {
  334. ChannelClientInfo cTemp = null;
  335. if (hChannelUserList.containsKey(oldNickname)) {
  336. cTemp = hChannelUserList.get(oldNickname);
  337. if (cTemp == cChannelClient) {
  338. // Remove the old key
  339. hChannelUserList.remove(oldNickname);
  340. // Add with the new key. (getNickname will return the new name not the
  341. // old one)
  342. hChannelUserList.put(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()), cTemp);
  343. }
  344. }
  345. }
  346. /**
  347. * Set the create time.
  348. *
  349. * @param nNewTime New unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  350. */
  351. protected void setCreateTime(final long nNewTime) { nCreateTime = nNewTime; }
  352. /**
  353. * Get the Create time.
  354. *
  355. * @return Unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  356. */
  357. public long getCreateTime() { return nCreateTime; }
  358. /**
  359. * Set the topic time.
  360. *
  361. * @param nNewTime New unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
  362. */
  363. protected void setTopicTime(final long nNewTime) { nTopicTime = nNewTime; }
  364. /**
  365. * Get the topic time.
  366. *
  367. * @return Unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
  368. */
  369. public long getTopicTime() { return nTopicTime; }
  370. /**
  371. * Set the topic.
  372. *
  373. * @param sNewTopic New contents of topic
  374. */
  375. protected void setTopic(final String sNewTopic) { sTopic = sNewTopic; }
  376. /**
  377. * Get the topic.
  378. *
  379. * @return contents of topic
  380. */
  381. public String getTopic() { return sTopic; }
  382. /**
  383. * Set the topic creator.
  384. *
  385. * @param sNewUser New user who set the topic (nickname if gotten on connect, full host if seen by parser)
  386. */
  387. protected void setTopicUser(final String sNewUser) { sTopicUser = sNewUser; }
  388. /**
  389. * Get the topic creator.
  390. *
  391. * @return user who set the topic (nickname if gotten on connect, full host if seen by parser)
  392. */
  393. public String getTopicUser() { return sTopicUser; }
  394. /**
  395. * Set the channel modes (as an integer).
  396. *
  397. * @param nNewMode new long representing channel modes. (Boolean only)
  398. */
  399. protected void setMode(final long nNewMode) { nModes = nNewMode; }
  400. /**
  401. * Get the channel modes (as an integer).
  402. *
  403. * @return long representing channel modes. (Boolean only)
  404. */
  405. public long getMode() { return nModes; }
  406. /**
  407. * Get the channel modes (as a string representation).
  408. *
  409. * @return string representing modes. (boolean and non-list)
  410. */
  411. public String getModeStr() {
  412. final StringBuilder sModes = new StringBuilder("+");
  413. final StringBuilder sModeParams = new StringBuilder();
  414. String sTemp = "";
  415. long nTemp = 0;
  416. final long nChanModes = this.getMode();
  417. for (char cTemp : myParser.hChanModesBool.keySet()) {
  418. nTemp = myParser.hChanModesBool.get(cTemp);
  419. if ((nChanModes & nTemp) == nTemp) { sModes.append(cTemp); }
  420. }
  421. for (char cTemp : hParamModes.keySet()) {
  422. sTemp = hParamModes.get(cTemp);
  423. if (!sTemp.isEmpty()) {
  424. sModes.append(cTemp);
  425. sModeParams.append(" ").append(this.getModeParam(cTemp));
  426. }
  427. }
  428. return sModes.append(sModeParams).toString();
  429. }
  430. /**
  431. * Set a channel mode that requires a parameter.
  432. *
  433. * @param cMode Character representing mode
  434. * @param sValue String repreenting value (if "" mode is unset)
  435. */
  436. protected void setModeParam(final Character cMode, final String sValue) {
  437. if (sValue.isEmpty()) {
  438. if (hParamModes.containsKey(cMode)) {
  439. hParamModes.remove(cMode);
  440. }
  441. } else {
  442. hParamModes.put(cMode, sValue);
  443. }
  444. }
  445. /**
  446. * Get the value of a mode that requires a parameter.
  447. *
  448. * @param cMode Character representing mode
  449. * @return string representing the value of the mode ("" if mode not set)
  450. */
  451. public String getModeParam(final Character cMode) {
  452. if (hParamModes.containsKey(cMode)) {
  453. return hParamModes.get(cMode);
  454. }
  455. return "";
  456. }
  457. /**
  458. * Add/Remove a value to a channel list.
  459. *
  460. * @param givenMode Character representing mode
  461. * @param givenItem ChannelListModeItem representing the item
  462. * @param bAdd Add or remove the value. (true for add, false for remove)
  463. */
  464. protected void setListModeParam(final Character givenMode, final ChannelListModeItem givenItem, final boolean bAdd) {
  465. Character cMode = givenMode;
  466. ChannelListModeItem newItem = givenItem;
  467. if (!myParser.hChanModesOther.containsKey(cMode) || myParser.hChanModesOther.get(cMode) != IRCParser.MODE_LIST) { return; }
  468. // Hyperion sucks.
  469. if (cMode == 'b' || cMode == 'q') {
  470. final String thisIRCD = myParser.getIRCD(true).toLowerCase();
  471. if ((thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"))) {
  472. if (cMode == 'b' && givenItem.getItem().charAt(0) == '%') {
  473. cMode = 'q';
  474. } else if (cMode == 'q' && givenItem.getItem().charAt(0) != '%') {
  475. cMode = 'b';
  476. }
  477. if (givenItem.getItem().charAt(0) == '%') {
  478. newItem = new ChannelListModeItem(givenItem.getItem().substring(1), givenItem.getOwner(), givenItem.getTime());
  479. }
  480. }
  481. }
  482. if (!hListModes.containsKey(cMode)) {
  483. hListModes.put(cMode, new ArrayList<ChannelListModeItem>());
  484. }
  485. final ArrayList<ChannelListModeItem> lModes = hListModes.get(cMode);
  486. for (int i = 0; i < lModes.size(); i++) {
  487. if (myParser.getIRCStringConverter().equalsIgnoreCase(lModes.get(i).getItem(), newItem.getItem())) {
  488. if (bAdd) { return; }
  489. else {
  490. lModes.remove(i);
  491. break;
  492. }
  493. }
  494. }
  495. if (bAdd) { lModes.add(newItem); }
  496. }
  497. /**
  498. * Get the list object representing a channel mode.
  499. *
  500. * @param cMode Character representing mode
  501. * @return ArrayList containing ChannelListModeItem in the list, or null if mode is invalid
  502. */
  503. public List<ChannelListModeItem> getListModeParam(final Character cMode) {
  504. if (!myParser.hChanModesOther.containsKey(cMode) || myParser.hChanModesOther.get(cMode) != myParser.MODE_LIST) { return null; }
  505. if (!hListModes.containsKey(cMode)) {
  506. hListModes.put(cMode, new ArrayList<ChannelListModeItem>());
  507. }
  508. return hListModes.get(cMode);
  509. }
  510. /**
  511. * Get the "adding state" of a list mode.
  512. *
  513. * @param cMode Character representing mode
  514. * @return false if we are not expecting a 367 etc, else true.
  515. */
  516. public boolean getAddState(final Character cMode) {
  517. synchronized (lAddingModes) {
  518. return lAddingModes.contains(cMode);
  519. }
  520. }
  521. /**
  522. * Get the "adding state" of a list mode.
  523. *
  524. * @param cMode Character representing mode
  525. * @param newState change the value returned by getAddState
  526. */
  527. protected void setAddState(final Character cMode, final boolean newState) {
  528. synchronized (lAddingModes) {
  529. if (newState) {
  530. lAddingModes.add(cMode);
  531. } else {
  532. if (lAddingModes.contains(cMode)) { lAddingModes.remove(cMode); }
  533. }
  534. }
  535. }
  536. /**
  537. * Reset the "adding state" of *all* list modes.
  538. */
  539. protected void resetAddState() {
  540. synchronized (lAddingModes) {
  541. lAddingModes.clear();
  542. }
  543. }
  544. /**
  545. * Adjust the channel modes on a channel.
  546. * This function will queue modes up to be sent in one go, according to 005 params.
  547. * If less modes are altered than the queue accepts, sendModes() must be called.<br><br>
  548. * sendModes is automatically called if you attempt to add more modes than is allowed
  549. * to be queued
  550. *
  551. * @param positive Is this a positive mode change, or a negative mode change
  552. * @param mode Character representing the mode to change
  553. * @param parameter Parameter needed to make change (not used if mode doesn't need a parameter)
  554. */
  555. public void alterMode(final boolean positive, final Character mode, final String parameter) {
  556. int modecount = 1;
  557. int modeint = 0;
  558. String modestr = "";
  559. if (myParser.h005Info.containsKey("MODES")) {
  560. try {
  561. modecount = Integer.parseInt(myParser.h005Info.get("MODES"));
  562. } catch (NumberFormatException e) {
  563. modecount = 1;
  564. }
  565. }
  566. if (!myParser.isUserSettable(mode)) { return; }
  567. modestr = ((positive) ? "+" : "-") + mode;
  568. if (myParser.hChanModesBool.containsKey(mode)) {
  569. final String teststr = ((positive) ? "-" : "+") + mode;
  570. if (lModeQueue.contains(teststr)) {
  571. lModeQueue.remove(teststr);
  572. return;
  573. } else if (lModeQueue.contains(modestr)) {
  574. return;
  575. }
  576. } else {
  577. // May need a param
  578. if (myParser.hPrefixModes.containsKey(mode)) {
  579. modestr = modestr + " " + parameter;
  580. } else {
  581. modeint = myParser.hChanModesOther.get(mode);
  582. if ((modeint & IRCParser.MODE_LIST) == IRCParser.MODE_LIST) {
  583. modestr = modestr + " " + parameter;
  584. } else if (!positive && ((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
  585. modestr = modestr + " " + parameter;
  586. } else if (positive && ((modeint & IRCParser.MODE_SET) == IRCParser.MODE_SET)) {
  587. // Does mode require a param to unset aswell?
  588. // We might need to queue an unset first
  589. if (((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
  590. final String existingParam = getModeParam(mode);
  591. if (!existingParam.isEmpty()) {
  592. final String reverseModeStr = "-" + mode + " " + existingParam;
  593. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", reverseModeStr);
  594. lModeQueue.add(reverseModeStr);
  595. if (lModeQueue.size() == modecount) { sendModes(); }
  596. }
  597. }
  598. modestr = modestr + " " + parameter;
  599. }
  600. }
  601. }
  602. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", modestr);
  603. lModeQueue.add(modestr);
  604. if (lModeQueue.size() == modecount) { sendModes(); }
  605. }
  606. /**
  607. * This function will send modes that are currently queued up to send.
  608. * This assumes that the queue only contains the amount that are alowed to be sent
  609. * and thus will try to send the entire queue in one go.<br><br>
  610. * Modes are always sent negative then positive and not mixed.
  611. */
  612. public void sendModes() {
  613. if (lModeQueue.isEmpty()) { return; }
  614. final StringBuilder positivemode = new StringBuilder();
  615. final StringBuilder positiveparam = new StringBuilder();
  616. final StringBuilder negativemode = new StringBuilder();
  617. final StringBuilder negativeparam = new StringBuilder();
  618. final StringBuilder sendModeStr = new StringBuilder();
  619. String modestr;
  620. String[] modeparam;
  621. boolean positive;
  622. for (int i = 0; i < lModeQueue.size(); ++i) {
  623. modeparam = lModeQueue.get(i).split(" ");
  624. modestr = modeparam[0];
  625. positive = modestr.charAt(0) == '+';
  626. if (positive) {
  627. positivemode.append(modestr.charAt(1));
  628. if (modeparam.length > 1) { positiveparam.append(" ").append(modeparam[1]); }
  629. } else {
  630. negativemode.append(modestr.charAt(1));
  631. if (modeparam.length > 1) { negativeparam.append(" ").append(modeparam[1]); }
  632. }
  633. }
  634. if (negativemode.length() > 0) { sendModeStr.append("-").append(negativemode); }
  635. if (positivemode.length() > 0) { sendModeStr.append("+").append(positivemode); }
  636. if (negativeparam.length() > 0) { sendModeStr.append(negativeparam); }
  637. if (positiveparam.length() > 0) { sendModeStr.append(positiveparam); }
  638. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Sending mode: %s", sendModeStr.toString());
  639. myParser.sendLine("MODE " + sName + " " + sendModeStr.toString());
  640. clearModeQueue();
  641. }
  642. /**
  643. * This function will clear the mode queue (WITHOUT Sending).
  644. */
  645. public void clearModeQueue() {
  646. lModeQueue.clear();
  647. }
  648. /**
  649. * Send a private message to the channel.
  650. *
  651. * @param sMessage Message to send
  652. */
  653. public void sendMessage(final String sMessage) {
  654. if (sMessage.isEmpty()) { return; }
  655. myParser.sendString("PRIVMSG " + sName + " :" + sMessage);
  656. }
  657. /**
  658. * Send a notice message to a target.
  659. *
  660. * @param sMessage Message to send
  661. */
  662. public void sendNotice(final String sMessage) {
  663. if (sMessage.isEmpty()) { return; }
  664. myParser.sendString("NOTICE " + sName + " :" + sMessage);
  665. }
  666. /**
  667. * Send a private message to a target.
  668. *
  669. * @param sMessage Message to send
  670. */
  671. public void sendAction(final String sMessage) {
  672. if (sMessage.isEmpty()) { return; }
  673. sendCTCP("ACTION", sMessage);
  674. }
  675. /**
  676. * Send a CTCP to a target.
  677. *
  678. * @param sType Type of CTCP
  679. * @param sMessage Optional Additional Parameters
  680. */
  681. public void sendCTCP(final String sType, String sMessage) {
  682. if (sType.isEmpty()) { return; }
  683. final char char1 = (char) 1;
  684. if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
  685. sendMessage(char1 + sType.toUpperCase() + sMessage + char1);
  686. }
  687. /**
  688. * Send a CTCPReply to a target.
  689. *
  690. * @param sType Type of CTCP
  691. * @param sMessage Optional Additional Parameters
  692. */
  693. public void sendCTCPReply(final String sType, String sMessage) {
  694. if (sType.isEmpty()) { return; }
  695. final char char1 = (char) 1;
  696. if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
  697. sendNotice(char1 + sType.toUpperCase() + sMessage + char1);
  698. }
  699. /**
  700. * Get a string representation of the Channel.
  701. *
  702. * @return String representation of the Channel.
  703. */
  704. @Override
  705. public String toString() { return sName; }
  706. /**
  707. * Get the parser object that owns this channel.
  708. *
  709. * @return The parser object that owns this channel
  710. */
  711. public IRCParser getParser() { return myParser; }
  712. }